Skip to content

Commit

Permalink
Flat ConfidentialComputeRequest (#65)
Browse files Browse the repository at this point in the history
* Flattens the confidential compute request
  • Loading branch information
Ruteri authored Oct 5, 2023
1 parent 5e254a8 commit 1d8705d
Show file tree
Hide file tree
Showing 17 changed files with 304 additions and 221 deletions.
90 changes: 55 additions & 35 deletions core/types/confidential.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,84 @@ import (
)

type ConfidentialComputeRequest struct {
ExecutionNode common.Address `json:"executionNode" gencodec:"required"`
Wrapped Transaction `json:"wrapped" gencodec:"required"`
ChainID *big.Int // Overwrite the wrapped chain id, since different txs handle it differently. Also probably should overwrite V,R,S
Nonce uint64
GasPrice *big.Int
Gas uint64
To *common.Address `rlp:"nil"`
Value *big.Int
Data []byte

ExecutionNode common.Address

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,
Wrapped: *NewTx(tx.Wrapped.inner),
ChainID: new(big.Int),

Value: new(big.Int),
GasPrice: new(big.Int),

ChainID: new(big.Int),
V: new(big.Int),
R: new(big.Int),
S: new(big.Int),
}

if tx.Value != nil {
cpy.Value.Set(tx.Value)
}
if tx.GasPrice != nil {
cpy.GasPrice.Set(tx.GasPrice)
}
if tx.ChainID != nil {
cpy.ChainID.Set(tx.ChainID)
}
if tx.V != nil {
cpy.V.Set(tx.V)
}
if tx.R != nil {
cpy.R.Set(tx.R)
}
if tx.S != nil {
cpy.S.Set(tx.S)
}

return cpy
}

// accessors for innerTx.
func (tx *ConfidentialComputeRequest) txType() byte {
return ConfidentialComputeRequestTxType
}

func (tx *ConfidentialComputeRequest) data() []byte {
return tx.Wrapped.Data()
}

// Rest is carried over from wrapped tx
func (tx *ConfidentialComputeRequest) chainID() *big.Int { return tx.ChainID }
func (tx *ConfidentialComputeRequest) accessList() AccessList { return tx.Wrapped.inner.accessList() }
func (tx *ConfidentialComputeRequest) gas() uint64 { return tx.Wrapped.inner.gas() }
func (tx *ConfidentialComputeRequest) gasFeeCap() *big.Int { return tx.Wrapped.inner.gasFeeCap() }
func (tx *ConfidentialComputeRequest) gasTipCap() *big.Int { return tx.Wrapped.inner.gasTipCap() }
func (tx *ConfidentialComputeRequest) gasPrice() *big.Int { return tx.Wrapped.inner.gasFeeCap() }
func (tx *ConfidentialComputeRequest) value() *big.Int { return tx.Wrapped.inner.value() }
func (tx *ConfidentialComputeRequest) nonce() uint64 { return tx.Wrapped.inner.nonce() }
func (tx *ConfidentialComputeRequest) to() *common.Address { return tx.Wrapped.inner.to() }
func (tx *ConfidentialComputeRequest) blobGas() uint64 { return tx.Wrapped.inner.blobGas() }
func (tx *ConfidentialComputeRequest) blobGasFeeCap() *big.Int {
return tx.Wrapped.inner.blobGasFeeCap()
}
func (tx *ConfidentialComputeRequest) blobHashes() []common.Hash {
return tx.Wrapped.inner.blobHashes()
}
func (tx *ConfidentialComputeRequest) txType() byte { return ConfidentialComputeRequestTxType }
func (tx *ConfidentialComputeRequest) chainID() *big.Int { return tx.ChainID }
func (tx *ConfidentialComputeRequest) accessList() AccessList { return nil }
func (tx *ConfidentialComputeRequest) data() []byte { return tx.Data }
func (tx *ConfidentialComputeRequest) gas() uint64 { return tx.Gas }
func (tx *ConfidentialComputeRequest) gasPrice() *big.Int { return tx.GasPrice }
func (tx *ConfidentialComputeRequest) gasTipCap() *big.Int { return tx.GasPrice }
func (tx *ConfidentialComputeRequest) gasFeeCap() *big.Int { return tx.GasPrice }
func (tx *ConfidentialComputeRequest) value() *big.Int { return tx.Value }
func (tx *ConfidentialComputeRequest) nonce() uint64 { return tx.Nonce }
func (tx *ConfidentialComputeRequest) to() *common.Address { return tx.To }
func (tx *ConfidentialComputeRequest) blobGas() uint64 { return 0 }
func (tx *ConfidentialComputeRequest) blobGasFeeCap() *big.Int { return nil }
func (tx *ConfidentialComputeRequest) blobHashes() []common.Hash { return nil }

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

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

func (tx *ConfidentialComputeRequest) setSignatureValues(chainID, v, r, s *big.Int) {
tx.ChainID = new(big.Int).Set(chainID)
tx.Wrapped.inner.setSignatureValues(chainID, v, r, s)
tx.ChainID, tx.V, tx.R, tx.S = chainID, v, r, s
}

type SuaveTransaction struct {
Expand Down
70 changes: 70 additions & 0 deletions core/types/confidential_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package types

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)

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

signer := NewSuaveSigner(new(big.Int))
unsignedTx := NewTx(&ConfidentialComputeRequest{})
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)
}

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

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

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

unsignedTx := NewTx(&SuaveTransaction{
ExecutionNode: crypto.PubkeyToAddress(testKey.PublicKey),
ConfidentialComputeRequest: *signedCCR,
})
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)
}
67 changes: 49 additions & 18 deletions core/types/transaction_marshalling.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ type txJSON struct {
AccessList *AccessList `json:"accessList,omitempty"`
BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
ExecutionNode *common.Address `json:"executionNode,omitempty"`
ConfidentialInputsHash common.Hash `json:"confidentialInputsHash,omitempty"`
Wrapped *json.RawMessage `json:"wrapped,omitempty"`
ConfidentialComputeResult *hexutil.Bytes `json:"confidentialComputeResult,omitempty"`
V *hexutil.Big `json:"v"`
Expand Down Expand Up @@ -118,14 +119,16 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {

case *ConfidentialComputeRequest:
enc.ExecutionNode = &itx.ExecutionNode

wrapped, err := itx.Wrapped.MarshalJSON()
if err != nil {
return nil, err
}

enc.Wrapped = (*json.RawMessage)(&wrapped)
enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
enc.To = tx.To()
enc.Gas = (*hexutil.Uint64)(&itx.Gas)
enc.GasPrice = (*hexutil.Big)(itx.GasPrice)
enc.Value = (*hexutil.Big)(itx.Value)
enc.Input = (*hexutil.Bytes)(&itx.Data)
enc.ChainID = (*hexutil.Big)(itx.ChainID)
enc.V = (*hexutil.Big)(itx.V)
enc.R = (*hexutil.Big)(itx.R)
enc.S = (*hexutil.Big)(itx.S)

case *SuaveTransaction:
enc.ExecutionNode = &itx.ExecutionNode
Expand Down Expand Up @@ -384,25 +387,53 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
if dec.ExecutionNode == nil {
return errors.New("missing required field 'executionNode' in transaction")
}

itx.ExecutionNode = *dec.ExecutionNode

if dec.Wrapped == nil {
return errors.New("missing required field 'wrapped' in transaction")
if dec.Nonce == nil {
return errors.New("missing required field 'nonce' in transaction")
}

var wrappedTx Transaction
err := wrappedTx.UnmarshalJSON(([]byte)(*dec.Wrapped))
if err != nil {
return err
itx.Nonce = uint64(*dec.Nonce)
if dec.To != nil {
itx.To = dec.To
}

itx.Wrapped = wrappedTx

if dec.Gas == nil {
return errors.New("missing required field 'gas' in transaction")
}
itx.Gas = uint64(*dec.Gas)
if dec.GasPrice == nil {
return errors.New("missing required field 'gasPrice' in transaction")
}
itx.GasPrice = (*big.Int)(dec.GasPrice)
if dec.Value == nil {
return errors.New("missing required field 'value' in transaction")
}
itx.Value = (*big.Int)(dec.Value)
if dec.Input == nil {
return errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
if dec.ChainID == nil {
return errors.New("missing required field 'chainId' in transaction")
}
itx.ChainID = (*big.Int)(dec.ChainID)
if dec.V == nil {
return errors.New("missing required field 'r' in transaction")
}
itx.V = (*big.Int)(dec.V)
if dec.R == nil {
return errors.New("missing required field 'r' in transaction")
}
itx.R = (*big.Int)(dec.R)
if dec.S == nil {
return errors.New("missing required field 's' in transaction")
}
itx.S = (*big.Int)(dec.S)
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
if withSignature {
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
return err
}
}

case SuaveTxType:
var itx SuaveTransaction
Expand Down
62 changes: 29 additions & 33 deletions core/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,10 @@ func (s suaveSigner) Sender(tx *Transaction) (common.Address, error) {
return s.londonSigner.Sender(tx)
}

var confidentialComputeRequestTx *Transaction = tx
var ccr *Transaction = tx
if tx.Type() == SuaveTxType { // Verify ExecutionNode's signature
inner := tx.inner.(*SuaveTransaction)
confidentialComputeRequestTx = &inner.ConfidentialComputeRequest
ccr = &inner.ConfidentialComputeRequest

V, R, S := tx.RawSignatureValues()
// DynamicFee txs are defined to use 0 and 1 as their recovery
Expand All @@ -291,20 +291,14 @@ func (s suaveSigner) Sender(tx *Transaction) (common.Address, error) {
}

{ // Verify wrapped tx's signature
V, R, S := confidentialComputeRequestTx.RawSignatureValues()
// compute requests are defined to use 0 and 1 as their recovery
V, R, S := ccr.RawSignatureValues()
// DynamicFee txs are defined to use 0 and 1 as their recovery
// id, add 27 to become equivalent to unprotected Homestead signatures.
V = new(big.Int).Add(V, big.NewInt(27))
if confidentialComputeRequestTx.ChainId().Cmp(s.chainId) != 0 {
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, confidentialComputeRequestTx.ChainId(), s.chainId)
}

addr, err := recoverPlain(s.Hash(confidentialComputeRequestTx), R, S, V, true)
if err != nil {
return common.Address{}, err
if ccr.ChainId().Cmp(s.chainId) != 0 {
return common.Address{}, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, ccr.ChainId(), s.chainId)
}

return addr, nil
return recoverPlain(s.Hash(ccr), R, S, V, true)
}
}

Expand All @@ -314,47 +308,49 @@ func (s suaveSigner) Equal(s2 Signer) bool {
}

func (s suaveSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) {
txdataConfidential, ok := tx.inner.(*ConfidentialComputeRequest)
if ok {
if txdataConfidential.ChainID.Sign() != 0 && txdataConfidential.ChainID.Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdataConfidential.ChainID, s.chainId)
switch txdata := tx.inner.(type) {
case *ConfidentialComputeRequest:
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
return R, S, V, nil
}

txdataExecuted, ok := tx.inner.(*SuaveTransaction)
if ok {
if txdataExecuted.ChainID.Sign() != 0 && txdataExecuted.ChainID.Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdataExecuted.ChainID, s.chainId)
case *SuaveTransaction:
if txdata.ChainID.Sign() != 0 && txdata.ChainID.Cmp(s.chainId) != 0 {
return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
}
R, S, _ = decodeSignature(sig)
V = big.NewInt(int64(sig[64]))
return R, S, V, nil
default:
return s.londonSigner.SignatureValues(tx, sig)
}

return s.londonSigner.SignatureValues(tx, sig)
}

// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s suaveSigner) Hash(tx *Transaction) common.Hash {
switch tx.Type() {
case ConfidentialComputeRequestTxType:
switch txdata := tx.inner.(type) {
case *ConfidentialComputeRequest:
return prefixedRlpHash(
tx.Type(),
[]interface{}{
tx.inner.(*ConfidentialComputeRequest).ExecutionNode,
s.Hash(&tx.inner.(*ConfidentialComputeRequest).Wrapped),
txdata.ExecutionNode,
tx.Nonce(),
tx.GasPrice(),
tx.Gas(),
tx.To(),
tx.Value(),
tx.Data(),
})
case SuaveTxType:
case *SuaveTransaction:
return prefixedRlpHash(
tx.Type(),
[]interface{}{
tx.inner.(*SuaveTransaction).ExecutionNode,
s.Hash(&tx.inner.(*SuaveTransaction).ConfidentialComputeRequest),
tx.inner.(*SuaveTransaction).ConfidentialComputeResult,
txdata.ExecutionNode,
s.Hash(&txdata.ConfidentialComputeRequest),
txdata.ConfidentialComputeResult,
})
default:
return s.londonSigner.Hash(tx)
Expand Down
Loading

0 comments on commit 1d8705d

Please sign in to comment.