From 656dd986267eb37551f1b231aa5b7f5e2850a868 Mon Sep 17 00:00:00 2001 From: Peter Broadhurst Date: Sun, 27 Oct 2024 15:22:06 -0400 Subject: [PATCH] Allow round-tripping of unsigned EIP-1559 signature payloads Signed-off-by: Peter Broadhurst --- pkg/ethsigner/transaction.go | 25 ++++++++++++++++------- pkg/ethsigner/transaction_test.go | 33 ++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/pkg/ethsigner/transaction.go b/pkg/ethsigner/transaction.go index 22ea6a0a..26a2bb6b 100644 --- a/pkg/ethsigner/transaction.go +++ b/pkg/ethsigner/transaction.go @@ -299,8 +299,7 @@ func recoverCommon(tx *Transaction, message []byte, chainID int64, v int64, r, s }, nil } -func RecoverEIP1559Transaction(ctx context.Context, rawTx ethtypes.HexBytes0xPrefix, chainID int64) (*ethtypes.Address0xHex, *TransactionWithOriginalPayload, error) { - +func decodeEIP1559SignaturePayload(ctx context.Context, rawTx ethtypes.HexBytes0xPrefix, chainID int64, rlpMinLen int) (rlp.List, *Transaction, error) { if len(rawTx) == 0 || rawTx[0] != TransactionType1559 { return nil, nil, i18n.NewError(ctx, signermsgs.MsgInvalidEIP1559Transaction, "TransactionType") } @@ -311,18 +310,17 @@ func RecoverEIP1559Transaction(ctx context.Context, rawTx ethtypes.HexBytes0xPre log.L(ctx).Errorf("Invalid EIP-1559 transaction data '%s': %s", rawTx, err) return nil, nil, i18n.NewError(ctx, signermsgs.MsgInvalidEIP1559Transaction, err) } + rlpList := decoded.(rlp.List) - if decoded == nil || len(decoded.(rlp.List)) < 12 { - log.L(ctx).Errorf("Invalid EIP-1559 transaction data '%s': EOF", rawTx) + if len(rlpList) < rlpMinLen { + log.L(ctx).Errorf("Invalid EIP-1559 transaction data (%d RLP elements)", rlpList) return nil, nil, i18n.NewError(ctx, signermsgs.MsgInvalidEIP1559Transaction, "EOF") } - rlpList := decoded.(rlp.List) - encodedChainID := rlpList[0].ToData().IntOrZero().Int64() if encodedChainID != chainID { return nil, nil, i18n.NewError(ctx, signermsgs.MsgInvalidChainID, chainID, encodedChainID) } - tx := &Transaction{ + return rlpList, &Transaction{ Nonce: (*ethtypes.HexInteger)(rlpList[1].ToData().Int()), MaxPriorityFeePerGas: (*ethtypes.HexInteger)(rlpList[2].ToData().Int()), MaxFeePerGas: (*ethtypes.HexInteger)(rlpList[3].ToData().Int()), @@ -331,6 +329,19 @@ func RecoverEIP1559Transaction(ctx context.Context, rawTx ethtypes.HexBytes0xPre Value: (*ethtypes.HexInteger)(rlpList[6].ToData().Int()), Data: ethtypes.HexBytes0xPrefix(rlpList[7].ToData()), // No access list support + }, nil +} + +func DecodeEIP1559SignaturePayload(ctx context.Context, rawTx ethtypes.HexBytes0xPrefix, chainID int64) (*Transaction, error) { + _, tx, err := decodeEIP1559SignaturePayload(ctx, rawTx, chainID, 9 /* no signature data */) + return tx, err +} + +func RecoverEIP1559Transaction(ctx context.Context, rawTx ethtypes.HexBytes0xPrefix, chainID int64) (*ethtypes.Address0xHex, *TransactionWithOriginalPayload, error) { + + rlpList, tx, err := decodeEIP1559SignaturePayload(ctx, rawTx, chainID, 12 /* with signature data */) + if err != nil { + return nil, nil, err } return recoverCommon(tx, diff --git a/pkg/ethsigner/transaction_test.go b/pkg/ethsigner/transaction_test.go index d34bd4e5..ee0020a9 100644 --- a/pkg/ethsigner/transaction_test.go +++ b/pkg/ethsigner/transaction_test.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Kaleido, Inc. +// Copyright © 2024 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -31,6 +31,7 @@ import ( "github.com/hyperledger/firefly-signer/pkg/secp256k1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" ) func TestEncodeExistingLegacyEIP155(t *testing.T) { @@ -294,6 +295,36 @@ func TestRecoverEIP1559BadStructure(t *testing.T) { assert.Regexp(t, "FF22084.*EOF", err) } +func TestDecodeEIP1559SignaturePayloadEmpty(t *testing.T) { + _, err := DecodeEIP1559SignaturePayload(context.Background(), []byte{}, 1001) + assert.Regexp(t, "FF22084.*TransactionType", err) +} + +func TestRoundTripEIP1559Payload(t *testing.T) { + + txIn := Transaction{ + Nonce: ethtypes.NewHexInteger64(3), + GasLimit: ethtypes.NewHexInteger64(40574), + MaxPriorityFeePerGas: ethtypes.NewHexInteger64(505050), + MaxFeePerGas: ethtypes.NewHexInteger64(606060), + To: ethtypes.MustNewAddress("0x497eedc4299dea2f2a364be10025d0ad0f702de3"), + Data: ethtypes.MustNewHexBytes0xPrefix("0xfeedbeef"), + Value: ethtypes.NewHexInteger64(100000000), + } + abiData := txIn.SignaturePayloadEIP1559(1001).Bytes() + + txOut, err := DecodeEIP1559SignaturePayload(context.Background(), abiData, 1001) + require.NoError(t, err) + + jsonIn, err := json.Marshal(txIn) + require.NoError(t, err) + jsonOut, err := json.Marshal(txOut) + require.NoError(t, err) + + require.JSONEq(t, string(jsonIn), string(jsonOut)) + +} + func TestRecoverEIP1559BadChainID(t *testing.T) { _, _, err := RecoverEIP1559Transaction(context.Background(), append([]byte{TransactionType1559}, (rlp.List{ rlp.WrapInt(big.NewInt(111)),