Skip to content

Commit

Permalink
Merge conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
ferranbt committed Oct 19, 2023
2 parents ae1c570 + dd3875e commit 5fa0f12
Show file tree
Hide file tree
Showing 20 changed files with 473 additions and 194 deletions.
100 changes: 63 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ Let’s take a look at how you can request confidential computation through an e

1. Pick your favorite execution node. You’ll need its URL and wallet address. Note that the execution node is fully trusted to provide the result of your confidential computation.

2. Craft your confidential computation request. This is a regular Ethereum transaction, where you specify the desired contract address and its (public) calldata. I’m assuming you have found or deployed a smart contract which you intend to call. Don’t sign the transaction quite yet!
2. Craft your confidential computation record. This is a regular Ethereum transaction (fields are similar to `LegacyTx`), where you specify the desired contract address and its (public) calldata. I’m assuming you have found or deployed a smart contract which you intend to call. Don’t sign the transaction quite yet!

```go
allowedPeekers := []common.Address{newBlockBidPeeker, newBundleBidPeeker, buildEthBlockPeeker} // express which contracts should have access to your data (by their addresses)
confidentialComputeRequestInner := &types.LegacyTx{
confidentialComputeRecord := &types.ConfidentialComputeRecord{
ExecutionNode: "0x4E2B0c0e428AE1CDE26d5BcF17Ba83f447068E5B",
Nonce: suaveAccNonce,
To: &newBundleBidAddress,
Value: nil,
Expand All @@ -66,20 +67,20 @@ Let’s take a look at how you can request confidential computation through an e
}
```

3. Wrap your regular transaction into the new `ConfidentialComputeRequest` transaction type, and specify the execution node’s wallet address as the `ExecutionNode` field. Sign the transaction with your wallet.
3. Wrap your compute record into a `ConfidentialComputeRequest` transaction type, and specify the confidential data.

```go
confidentialDataBytes := hexutil.Encode(ethBundle)
confidentialComputeRequest := types.SignTx(types.NewTx(&types.ConfidentialComputeRequest{
ExecutionNode: "0x4E2B0c0e428AE1CDE26d5BcF17Ba83f447068E5B",
Wrapped: *types.NewTx(&confidentialComputeRequestInner),
ConfidentialComputeRecord: confidentialComputeRecord,
ConfidentialInputs: confidentialDataBytes,
}), suaveSigner, privKey)
```

4. Request confidential computation by submitting your transaction along with your confidential data to the execution node you chose via `eth_sendRawTransaction`.
4. Request confidential computation by submitting your transaction to the execution node you chose via `eth_sendRawTransaction`.

```go
confidentialDataBytes := hexutil.Encode(ethBundle)
suaveClient.Call("eth_sendRawTransaction", confidentialComputeRequest, confidentialDataBytes)
suaveClient.Call("eth_sendRawTransaction", confidentialComputeRequest)
```

5. All done! Once the execution node processes your computation request, the execution node will submit it as `SuaveTransaction` to the mempool.
Expand Down Expand Up @@ -180,44 +181,69 @@ Other than ability to access new precompiles, the contracts aiming to be execute
### Confidential compute requests
We introduce two new transaction types: `ConfidentialComputeRequest`, serving as a request of confidential computation, and `SuaveTransaction` which is the result of a confidential computation. The new confidential computation transactions track the usage of gas during confidential computation, and contain (or reference) the result of the computation in a chain-friendly manner.
We introduce a few new transaction types.
![image](suave/docs/conf_comp_request_flow.png)
* `ConfidentialComputeRecord`
confidential compute requests (`ConfidentialComputeRequest`) are only intermediary messages between the user requesting confidential computation and the execution node, and are not currently propagated through the mempool or included in blocks. The results of those computations (`SuaveTransaction`) are treated as regular transactions.
This type serves as an onchain record of computation. It's a part of both the [Confidential Compute Request](#confidential-compute-request) and [Suave Transaction](#suave-transaction).

```go
type ConfidentialComputeRequest struct {
ExecutionNode common.Address
Wrapped Transaction
}
```
```go
type ConfidentialComputeRecord struct {
ExecutionNode common.Address
ConfidentialInputsHash common.Hash
// LegacyTx fields
Nonce uint64
GasPrice *big.Int
Gas uint64
To *common.Address `rlp:"nil"`
Value *big.Int
Data []byte
// Signature fields
}
```

`SuaveTransaction` transactions are propagated through the mempool and inserted into blocks as expected, unifying confidential computation with regular on-chain execution.
* `ConfidentialComputeRequest`

```go
type SuaveTransaction struct {
ExecutionNode common.Address
ConfidentialComputeRequest Transaction
ConfidentialComputeResult []byte
/* Execution node's signature fields */
}
```
This type facilitates users in interacting with the MEVM through the `eth_sendRawTransaction` method. After processing, the request's `ConfidentialComputeRecord` is embedded into `SuaveTransaction.ConfidentialComputeRequest` and serves as an onchain record of computation.
The confidential computation result is placed in the `ConfidentialComputeResult` field, which is further used instead of the original transaction's calldata for on-chain execution.
```go
type ConfidentialComputeRequest struct {
ConfidentialComputeRecord
ConfidentialInputs []byte
}
```
The basic flow is as follows:
* `SuaveTransaction`
1. User crafts a usual legacy/dynamic transaction, which calls the contract of their liking
2. User crafts the confidential computation request (`ConfidentialComputeRequest`):
1. User choses an execution node of their liking, that is an address whose signature over the confidential computation result will be trusted
2. User embeds the transaction from (1.) into an `ConfidentialComputeRequest` together with the desired execution node's address
3. User signs and sends the confidential computation request to an execution node via `eth_sendRawTransaction` (possibly passing in additional confidential data)
3. The execution node executes the transaction in the confidential mode, providing access to the usual confidential APIs
4. The execution node creates a `SuaveTransaction` using the confidential computation request and the result of its execution, the node then signs and submits the transaction into the mempool
5. The transaction makes its way into a block, by executing the `ConfidentialComputeResult` as calldata, as long as the execution node's signature matches the requested executor node in (2.1.)
A specialized transaction type that encapsulates the result of a confidential computation request. It includes the `ConfidentialComputeRequest`, signed by the user, which ensures that the result comes from the expected computor, as the `SuaveTransaction`'s signer must match the `ExecutionNode`.

```go
type SuaveTransaction struct {
ExecutionNode common.Address
ConfidentialComputeRequest ConfidentialComputeRecord
ConfidentialComputeResult []byte
/* Execution node's signature fields */
}
```

![image](suave/docs/conf_comp_request_flow.png)


The basic flow is as follows:

The user passes in any confidential data through the new `confidential_data` parameter of the `eth_sendRawTransaction` RPC method. The initial confidential computation has access to both the public and confidential data, but only the public data becomes part of the transaction propagated through the mempool. Any confidential data passed in by the user is discarded after the execution.
1. User crafts a confidential computation request (`ConfidentialComputeRequest`):
1. Sets their GasPrice, GasLimit, To address and calldata as they would for a `LegacyTx`
2. Choses an execution node of their liking, that is an address whose signature over the confidential computation result will be trusted
3. The above becomes the `ConfidentialComputeRecord` that will eventually make its way onto the chain
4. Sets the `ConfidentialInputs` of the request (if any)
5. Signs and sends the confidential computation request (consisting of (3 and 4) to an execution node via `eth_sendRawTransaction`
2. The execution node executes the transaction in the confidential mode, providing access to the usual confidential APIs
3. The execution node creates a `SuaveTransaction` using the confidential computation request and the result of its execution, the node then signs and submits the transaction into the mempool
4. The transaction makes its way into a block, by executing the `ConfidentialComputeResult` as calldata, as long as the execution node's signature matches the requested executor node in (1.2.)
The initial confidential computation has access to both the public and confidential data, but only the public data becomes part of the transaction propagated through the mempool. Any confidential data passed in by the user is discarded after the execution.
Architecture reference
![image](suave/docs/execution_node_architecture.png)
Expand Down
94 changes: 69 additions & 25 deletions core/types/confidential.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,30 @@ import (
"github.com/ethereum/go-ethereum/common"
)

type ConfidentialComputeRequest struct {
type ConfidentialComputeRecord struct {
Nonce uint64
GasPrice *big.Int
Gas uint64
To *common.Address `rlp:"nil"`
Value *big.Int
Data []byte

ExecutionNode common.Address
ExecutionNode common.Address
ConfidentialInputsHash common.Hash

ChainID *big.Int
V, R, S *big.Int
}

// copy creates a deep copy of the transaction data and initializes all fields.
func (tx *ConfidentialComputeRequest) copy() TxData {
cpy := &ConfidentialComputeRequest{
Nonce: tx.Nonce,
To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data),
Gas: tx.Gas,
ExecutionNode: tx.ExecutionNode,
func (tx *ConfidentialComputeRecord) copy() TxData {
cpy := &ConfidentialComputeRecord{
Nonce: tx.Nonce,
To: copyAddressPtr(tx.To),
Data: common.CopyBytes(tx.Data),
Gas: tx.Gas,
ExecutionNode: tx.ExecutionNode,
ConfidentialInputsHash: tx.ConfidentialInputsHash,

Value: new(big.Int),
GasPrice: new(big.Int),
Expand Down Expand Up @@ -60,6 +62,48 @@ func (tx *ConfidentialComputeRequest) copy() TxData {
return cpy
}

func (tx *ConfidentialComputeRecord) txType() byte { return ConfidentialComputeRecordTxType }
func (tx *ConfidentialComputeRecord) chainID() *big.Int { return tx.ChainID }
func (tx *ConfidentialComputeRecord) accessList() AccessList { return nil }
func (tx *ConfidentialComputeRecord) data() []byte { return tx.Data }
func (tx *ConfidentialComputeRecord) gas() uint64 { return tx.Gas }
func (tx *ConfidentialComputeRecord) gasPrice() *big.Int { return tx.GasPrice }
func (tx *ConfidentialComputeRecord) gasTipCap() *big.Int { return tx.GasPrice }
func (tx *ConfidentialComputeRecord) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *ConfidentialComputeRecord) value() *big.Int { return tx.Value }
func (tx *ConfidentialComputeRecord) nonce() uint64 { return tx.Nonce }
func (tx *ConfidentialComputeRecord) to() *common.Address { return tx.To }
func (tx *ConfidentialComputeRecord) blobGas() uint64 { return 0 }
func (tx *ConfidentialComputeRecord) blobGasFeeCap() *big.Int { return nil }
func (tx *ConfidentialComputeRecord) blobHashes() []common.Hash { return nil }

func (tx *ConfidentialComputeRecord) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return dst.Set(tx.GasPrice)
}

func (tx *ConfidentialComputeRecord) rawSignatureValues() (v, r, s *big.Int) {
return tx.V, tx.R, tx.S
}

func (tx *ConfidentialComputeRecord) setSignatureValues(chainID, v, r, s *big.Int) {
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
}

type ConfidentialComputeRequest struct {
ConfidentialComputeRecord
ConfidentialInputs []byte
}

// copy creates a deep copy of the transaction data and initializes all fields.
func (tx *ConfidentialComputeRequest) copy() TxData {
cpy := &ConfidentialComputeRequest{
ConfidentialComputeRecord: *(tx.ConfidentialComputeRecord.copy().(*ConfidentialComputeRecord)),
ConfidentialInputs: tx.ConfidentialInputs,
}

return cpy
}

func (tx *ConfidentialComputeRequest) txType() byte { return ConfidentialComputeRequestTxType }
func (tx *ConfidentialComputeRequest) chainID() *big.Int { return tx.ChainID }
func (tx *ConfidentialComputeRequest) accessList() AccessList { return nil }
Expand Down Expand Up @@ -88,9 +132,9 @@ func (tx *ConfidentialComputeRequest) setSignatureValues(chainID, v, r, s *big.I
}

type SuaveTransaction struct {
ExecutionNode common.Address `json:"executionNode" gencodec:"required"`
ConfidentialComputeRequest Transaction `json:"confidentialComputeRequest" gencodec:"required"`
ConfidentialComputeResult []byte `json:"confidentialComputeResult" gencodec:"required"`
ExecutionNode common.Address `json:"executionNode" gencodec:"required"`
ConfidentialComputeRequest ConfidentialComputeRecord `json:"confidentialComputeRequest" gencodec:"required"`
ConfidentialComputeResult []byte `json:"confidentialComputeResult" gencodec:"required"`

// ExecutionNode's signature
ChainID *big.Int
Expand All @@ -103,7 +147,7 @@ type SuaveTransaction struct {
func (tx *SuaveTransaction) copy() TxData {
cpy := &SuaveTransaction{
ExecutionNode: tx.ExecutionNode,
ConfidentialComputeRequest: *NewTx(tx.ConfidentialComputeRequest.inner),
ConfidentialComputeRequest: tx.ConfidentialComputeRequest,
ConfidentialComputeResult: common.CopyBytes(tx.ConfidentialComputeResult),
ChainID: new(big.Int),
V: new(big.Int),
Expand Down Expand Up @@ -140,31 +184,31 @@ func (tx *SuaveTransaction) data() []byte {
// Rest is carried over from wrapped tx
func (tx *SuaveTransaction) chainID() *big.Int { return tx.ChainID }
func (tx *SuaveTransaction) accessList() AccessList {
return tx.ConfidentialComputeRequest.inner.accessList()
return tx.ConfidentialComputeRequest.accessList()
}
func (tx *SuaveTransaction) gas() uint64 { return tx.ConfidentialComputeRequest.inner.gas() }
func (tx *SuaveTransaction) gas() uint64 { return tx.ConfidentialComputeRequest.gas() }
func (tx *SuaveTransaction) gasFeeCap() *big.Int {
return tx.ConfidentialComputeRequest.inner.gasFeeCap()
return tx.ConfidentialComputeRequest.gasFeeCap()
}
func (tx *SuaveTransaction) gasTipCap() *big.Int {
return tx.ConfidentialComputeRequest.inner.gasTipCap()
return tx.ConfidentialComputeRequest.gasTipCap()
}
func (tx *SuaveTransaction) gasPrice() *big.Int {
return tx.ConfidentialComputeRequest.inner.gasFeeCap()
return tx.ConfidentialComputeRequest.gasFeeCap()
}
func (tx *SuaveTransaction) value() *big.Int { return tx.ConfidentialComputeRequest.inner.value() }
func (tx *SuaveTransaction) nonce() uint64 { return tx.ConfidentialComputeRequest.inner.nonce() }
func (tx *SuaveTransaction) to() *common.Address { return tx.ConfidentialComputeRequest.inner.to() }
func (tx *SuaveTransaction) blobGas() uint64 { return tx.ConfidentialComputeRequest.inner.blobGas() }
func (tx *SuaveTransaction) value() *big.Int { return tx.ConfidentialComputeRequest.value() }
func (tx *SuaveTransaction) nonce() uint64 { return tx.ConfidentialComputeRequest.nonce() }
func (tx *SuaveTransaction) to() *common.Address { return tx.ConfidentialComputeRequest.to() }
func (tx *SuaveTransaction) blobGas() uint64 { return tx.ConfidentialComputeRequest.blobGas() }
func (tx *SuaveTransaction) blobGasFeeCap() *big.Int {
return tx.ConfidentialComputeRequest.inner.blobGasFeeCap()
return tx.ConfidentialComputeRequest.blobGasFeeCap()
}
func (tx *SuaveTransaction) blobHashes() []common.Hash {
return tx.ConfidentialComputeRequest.inner.blobHashes()
return tx.ConfidentialComputeRequest.blobHashes()
}

func (tx *SuaveTransaction) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
return tx.ConfidentialComputeRequest.inner.effectiveGasPrice(dst, baseFee)
return tx.ConfidentialComputeRequest.effectiveGasPrice(dst, baseFee)
}

func (tx *SuaveTransaction) rawSignatureValues() (v, r, s *big.Int) {
Expand Down
46 changes: 44 additions & 2 deletions core/types/confidential_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,45 @@ import (
"github.com/stretchr/testify/require"
)

func TestCCRequestToRecord(t *testing.T) {
testKey, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
require.NoError(t, err)

signer := NewSuaveSigner(new(big.Int))
unsignedTx := NewTx(&ConfidentialComputeRequest{
ConfidentialComputeRecord: ConfidentialComputeRecord{
ExecutionNode: crypto.PubkeyToAddress(testKey.PublicKey),
},
ConfidentialInputs: []byte{0x46},
})
signedTx, err := SignTx(unsignedTx, signer, testKey)
require.NoError(t, err)

recoveredSender, err := signer.Sender(signedTx)
require.NoError(t, err)

require.Equal(t, crypto.PubkeyToAddress(testKey.PublicKey), recoveredSender)

marshalledTxBytes, err := signedTx.MarshalBinary()
require.NoError(t, err)

unmarshalledTx := new(Transaction)
require.NoError(t, unmarshalledTx.UnmarshalBinary(marshalledTxBytes))

recoveredUnmarshalledSender, err := signer.Sender(unmarshalledTx)
require.NoError(t, err)

require.Equal(t, crypto.PubkeyToAddress(testKey.PublicKey), recoveredUnmarshalledSender)

signedRequestInner, ok := CastTxInner[*ConfidentialComputeRequest](unmarshalledTx)
require.True(t, ok)

recoveredRecordSender, err := signer.Sender(NewTx(&signedRequestInner.ConfidentialComputeRecord))
require.NoError(t, err)

require.Equal(t, crypto.PubkeyToAddress(testKey.PublicKey), recoveredRecordSender)
}

func TestCCR(t *testing.T) {
testKey, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
require.NoError(t, err)
Expand Down Expand Up @@ -40,14 +79,17 @@ func TestSuaveTx(t *testing.T) {

signer := NewSuaveSigner(new(big.Int))

signedCCR, err := SignTx(NewTx(&ConfidentialComputeRequest{
signedCCR, err := SignTx(NewTx(&ConfidentialComputeRecord{
ExecutionNode: crypto.PubkeyToAddress(testKey.PublicKey),
}), signer, testKey)
require.NoError(t, err)

signedInnerCCR, ok := CastTxInner[*ConfidentialComputeRecord](signedCCR)
require.True(t, ok)

unsignedTx := NewTx(&SuaveTransaction{
ExecutionNode: crypto.PubkeyToAddress(testKey.PublicKey),
ConfidentialComputeRequest: *signedCCR,
ConfidentialComputeRequest: *signedInnerCCR,
})
signedTx, err := SignTx(unsignedTx, signer, testKey)
require.NoError(t, err)
Expand Down
5 changes: 3 additions & 2 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ const (
AccessListTxType = 0x01
DynamicFeeTxType = 0x02
BlobTxType = 0x03
ConfidentialComputeRequestTxType = 0x42
SuaveTxType = 0x43
ConfidentialComputeRecordTxType = 0x42
ConfidentialComputeRequestTxType = 0x43
SuaveTxType = 0x50
)

// Transaction is an Ethereum transaction.
Expand Down
Loading

0 comments on commit 5fa0f12

Please sign in to comment.