diff --git a/pkg/abi/abi.go b/pkg/abi/abi.go index 8e4dc6a2..9aaff0fb 100644 --- a/pkg/abi/abi.go +++ b/pkg/abi/abi.go @@ -340,29 +340,40 @@ func (a ABI) ErrorString(revertData []byte) (string, bool) { return a.ErrorStringCtx(context.Background(), revertData) } -func (a ABI) ErrorStringCtx(ctx context.Context, revertData []byte) (string, bool) { - var parsed []interface{} +func (a ABI) ErrorStringCtx(ctx context.Context, revertData []byte) (strError string, ok bool) { e, cv, ok := a.ParseErrorCtx(ctx, revertData) if ok { - if res, err := NewSerializer().SetFormattingMode(FormatAsFlatArrays).SerializeInterfaceCtx(ctx, cv); err == nil { - parsed, ok = res.([]interface{}) - } + strError = FormatErrorStringCtx(ctx, e, cv) + ok = strError != "" } - if !ok || parsed == nil { - return "", false + return strError, ok +} + +func FormatErrorStringCtx(ctx context.Context, e *Entry, cv *ComponentValue) string { + var ok bool + var parsed []interface{} + if res, err := NewSerializer(). + SetFormattingMode(FormatAsFlatArrays). + SetIntSerializer(Base10StringIntSerializer). + SetByteSerializer(HexByteSerializer0xPrefix). + SetAddressSerializer(HexAddrSerializer0xPrefix). + SerializeInterfaceCtx(ctx, cv); err == nil { + parsed, ok = res.([]interface{}) } - buff := new(bytes.Buffer) - buff.WriteString(e.Name) - buff.WriteRune('(') - for i, c := range parsed { - if i > 0 { - buff.WriteRune(',') + buff := new(strings.Builder) + if ok && parsed != nil { + buff.WriteString(e.Name) + buff.WriteRune('(') + for i, c := range parsed { + if i > 0 { + buff.WriteRune(',') + } + b, _ := json.Marshal(c) + buff.Write(b) } - b, _ := json.Marshal(c) - buff.Write(b) + buff.WriteRune(')') } - buff.WriteRune(')') - return buff.String(), true + return buff.String() } // Validate processes all the components of all the parameters in this ABI entry 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)),