From 6e6441dafdd44e4b4aceb1ce5f887a3bbffd1324 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Wed, 29 Jan 2025 16:49:31 -0600 Subject: [PATCH] refactor --- .../ccip/ccipevm/extradatadecoder.go | 56 ++++++++++ .../ccip/ccipevm/extradatadecoder_test.go | 105 ++++++++++++++++++ core/capabilities/ccip/ccipevm/helpers.go | 50 --------- .../capabilities/ccip/ccipevm/helpers_test.go | 54 --------- .../{helpers.go => extradatadecoder.go} | 0 ...lpers_test.go => extradatadecoder_test.go} | 0 6 files changed, 161 insertions(+), 104 deletions(-) create mode 100644 core/capabilities/ccip/ccipevm/extradatadecoder.go create mode 100644 core/capabilities/ccip/ccipevm/extradatadecoder_test.go rename core/capabilities/ccip/ccipsolana/{helpers.go => extradatadecoder.go} (100%) rename core/capabilities/ccip/ccipsolana/{helpers_test.go => extradatadecoder_test.go} (100%) diff --git a/core/capabilities/ccip/ccipevm/extradatadecoder.go b/core/capabilities/ccip/ccipevm/extradatadecoder.go new file mode 100644 index 00000000000..8f1c8616878 --- /dev/null +++ b/core/capabilities/ccip/ccipevm/extradatadecoder.go @@ -0,0 +1,56 @@ +package ccipevm + +import ( + "fmt" + + cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" +) + +func DecodeDestExecDataToMap(DestExecData cciptypes.Bytes) (map[string]interface{}, error) { + destGasAmount, err := abiDecodeUint32(DestExecData) + if err != nil { + return nil, fmt.Errorf("decode dest gas amount: %w", err) + } + + return map[string]interface{}{ + evmDestExecDataKey: destGasAmount, + }, nil +} + +func DecodeExtraArgsToMap(extraArgs []byte) (map[string]any, error) { + if len(extraArgs) < 4 { + return nil, fmt.Errorf("extra args too short: %d, should be at least 4 (i.e the extraArgs tag)", len(extraArgs)) + } + + var method string + var extraByteOffset int + switch string(extraArgs[:4]) { + case string(evmExtraArgsV1Tag): + // for EVMExtraArgs, the first four bytes is the method name + method = evmV1DecodeName + extraByteOffset = 4 + case string(evmExtraArgsV2Tag): + method = evmV2DecodeName + extraByteOffset = 4 + case string(svmExtraArgsV1Tag): + // for SVMExtraArgs there's the four bytes plus another 32 bytes padding for the dynamic array + // https://github.com/smartcontractkit/chainlink/blob/33c0bda696b0ed97f587a46eacd5c65bed9fb2c1/contracts/src/v0.8/ccip/libraries/Client.sol#L57 + // this is a temporary solution, the evm on-chain side will fix it, so the offset should just be 4 instead of 36 + method = svmV1DecodeName + extraByteOffset = 36 + default: + return nil, fmt.Errorf("unknown extra args tag: %x", extraArgs) + } + + output := make(map[string]any) + args := make(map[string]interface{}) + err := messageHasherABI.Methods[method].Inputs.UnpackIntoMap(args, extraArgs[extraByteOffset:]) + if err != nil { + return nil, fmt.Errorf("abi decode extra args %v: %w", method, err) + } + + for k, val := range args { + output[k] = val + } + return output, nil +} diff --git a/core/capabilities/ccip/ccipevm/extradatadecoder_test.go b/core/capabilities/ccip/ccipevm/extradatadecoder_test.go new file mode 100644 index 00000000000..e0387ed9040 --- /dev/null +++ b/core/capabilities/ccip/ccipevm/extradatadecoder_test.go @@ -0,0 +1,105 @@ +package ccipevm + +import ( + "math/big" + "math/rand" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/message_hasher" +) + +func Test_decodeExtraData(t *testing.T) { + d := testSetup(t) + gasLimit := big.NewInt(rand.Int63()) + + t.Run("decode extra args into map evm v1", func(t *testing.T) { + encoded, err := d.contract.EncodeEVMExtraArgsV1(nil, message_hasher.ClientEVMExtraArgsV1{ + GasLimit: gasLimit, + }) + require.NoError(t, err) + + m, err := DecodeExtraArgsToMap(encoded) + require.NoError(t, err) + require.Len(t, m, 1) + + gl, exist := m["gasLimit"] + require.True(t, exist) + require.Equal(t, gl, gasLimit) + }) + + t.Run("decode extra args into map evm v2", func(t *testing.T) { + encoded, err := d.contract.EncodeEVMExtraArgsV2(nil, message_hasher.ClientEVMExtraArgsV2{ + GasLimit: gasLimit, + AllowOutOfOrderExecution: true, + }) + require.NoError(t, err) + + m, err := DecodeExtraArgsToMap(encoded) + require.NoError(t, err) + require.Len(t, m, 2) + + gl, exist := m["gasLimit"] + require.True(t, exist) + require.Equal(t, gl, gasLimit) + + ooe, exist := m["allowOutOfOrderExecution"] + require.True(t, exist) + require.Equal(t, true, ooe) + }) + + t.Run("decode extra args into map svm", func(t *testing.T) { + key, err := solana.NewRandomPrivateKey() + require.NoError(t, err) + cu := uint32(10000) + bitmap := uint64(4) + ooe := false + tokenReceiver := [32]byte(key.PublicKey().Bytes()) + accounts := [][32]byte{[32]byte(key.PublicKey().Bytes())} + decoded, err := d.contract.DecodeSVMExtraArgsV1(nil, cu, bitmap, ooe, tokenReceiver, accounts) + if err != nil { + return + } + encoded, err := d.contract.EncodeSVMExtraArgsV1(nil, decoded) + require.NoError(t, err) + + m, err := DecodeExtraArgsToMap(encoded) + require.NoError(t, err) + require.Len(t, m, 5) + + cuDecoded, exist := m["computeUnits"] + require.True(t, exist) + require.Equal(t, cuDecoded, cu) + + bitmapDecoded, exist := m["accountIsWritableBitmap"] + require.True(t, exist) + require.Equal(t, bitmapDecoded, bitmap) + + ooeDecoded, exist := m["allowOutOfOrderExecution"] + require.True(t, exist) + require.Equal(t, ooeDecoded, ooe) + + tokenReceiverDecoded, exist := m["tokenReceiver"] + require.True(t, exist) + require.Equal(t, tokenReceiverDecoded, tokenReceiver) + + accountsDecoded, exist := m["accounts"] + require.True(t, exist) + require.Equal(t, accountsDecoded, accounts) + }) + + t.Run("decode dest exec data into map", func(t *testing.T) { + destGasAmount := uint32(10000) + encoded, err := abiEncodeUint32(destGasAmount) + require.NoError(t, err) + m, err := DecodeDestExecDataToMap(encoded) + require.NoError(t, err) + require.Len(t, m, 1) + + decoded, exist := m[evmDestExecDataKey] + require.True(t, exist) + require.Equal(t, destGasAmount, decoded) + }) +} diff --git a/core/capabilities/ccip/ccipevm/helpers.go b/core/capabilities/ccip/ccipevm/helpers.go index a00eed968a8..13e635ecbdf 100644 --- a/core/capabilities/ccip/ccipevm/helpers.go +++ b/core/capabilities/ccip/ccipevm/helpers.go @@ -6,7 +6,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/accounts/abi" - cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" ) const ( @@ -51,55 +50,6 @@ func decodeExtraArgsV1V2(extraArgs []byte) (gasLimit *big.Int, err error) { return ifaces[0].(*big.Int), nil } -func DecodeDestExecDataToMap(DestExecData cciptypes.Bytes) (map[string]interface{}, error) { - destGasAmount, err := abiDecodeUint32(DestExecData) - if err != nil { - return nil, fmt.Errorf("decode dest gas amount: %w", err) - } - - return map[string]interface{}{ - evmDestExecDataKey: destGasAmount, - }, nil -} - -func DecodeExtraArgsToMap(extraArgs []byte) (map[string]any, error) { - if len(extraArgs) < 4 { - return nil, fmt.Errorf("extra args too short: %d, should be at least 4 (i.e the extraArgs tag)", len(extraArgs)) - } - - var method string - var extraByteOffset int - switch string(extraArgs[:4]) { - case string(evmExtraArgsV1Tag): - // for EVMExtraArgs, the first four bytes is the method name - method = evmV1DecodeName - extraByteOffset = 4 - case string(evmExtraArgsV2Tag): - method = evmV2DecodeName - extraByteOffset = 4 - case string(svmExtraArgsV1Tag): - // for SVMExtraArgs there's the four bytes plus another 32 bytes padding for the dynamic array - // https://github.com/smartcontractkit/chainlink/blob/33c0bda696b0ed97f587a46eacd5c65bed9fb2c1/contracts/src/v0.8/ccip/libraries/Client.sol#L57 - // this is a temporary solution, the evm on-chain side will fix it, so the offset should just be 4 instead of 36 - method = svmV1DecodeName - extraByteOffset = 36 - default: - return nil, fmt.Errorf("unknown extra args tag: %x", extraArgs) - } - - output := make(map[string]any) - args := make(map[string]interface{}) - err := messageHasherABI.Methods[method].Inputs.UnpackIntoMap(args, extraArgs[extraByteOffset:]) - if err != nil { - return nil, fmt.Errorf("abi decode extra args %v: %w", method, err) - } - - for k, val := range args { - output[k] = val - } - return output, nil -} - // abiEncodeMethodInputs encodes the inputs for a method call. // example abi: `[{ "name" : "method", "type": "function", "inputs": [{"name": "a", "type": "uint256"}]}]` func abiEncodeMethodInputs(abiDef abi.ABI, inputs ...interface{}) ([]byte, error) { diff --git a/core/capabilities/ccip/ccipevm/helpers_test.go b/core/capabilities/ccip/ccipevm/helpers_test.go index f1490a56043..89580c4d6f7 100644 --- a/core/capabilities/ccip/ccipevm/helpers_test.go +++ b/core/capabilities/ccip/ccipevm/helpers_test.go @@ -5,7 +5,6 @@ import ( "math/rand" "testing" - "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/message_hasher" @@ -74,57 +73,4 @@ func Test_decodeExtraArgs(t *testing.T) { require.True(t, exist) require.Equal(t, true, ooe) }) - - t.Run("decode dest exec data into map", func(t *testing.T) { - destGasAmount := uint32(10000) - encoded, err := abiEncodeUint32(destGasAmount) - require.NoError(t, err) - m, err := DecodeDestExecDataToMap(encoded) - require.NoError(t, err) - require.Len(t, m, 1) - - decoded, exist := m[evmDestExecDataKey] - require.True(t, exist) - require.Equal(t, destGasAmount, decoded) - }) - - t.Run("decode extra args into map svm", func(t *testing.T) { - key, err := solana.NewRandomPrivateKey() - require.NoError(t, err) - cu := uint32(10000) - bitmap := uint64(4) - ooe := false - tokenReceiver := [32]byte(key.PublicKey().Bytes()) - accounts := [][32]byte{[32]byte(key.PublicKey().Bytes())} - decoded, err := d.contract.DecodeSVMExtraArgsV1(nil, cu, bitmap, ooe, tokenReceiver, accounts) - if err != nil { - return - } - encoded, err := d.contract.EncodeSVMExtraArgsV1(nil, decoded) - require.NoError(t, err) - - m, err := DecodeExtraArgsToMap(encoded) - require.NoError(t, err) - require.Len(t, m, 5) - - cuDecoded, exist := m["computeUnits"] - require.True(t, exist) - require.Equal(t, cuDecoded, cu) - - bitmapDecoded, exist := m["accountIsWritableBitmap"] - require.True(t, exist) - require.Equal(t, bitmapDecoded, bitmap) - - ooeDecoded, exist := m["allowOutOfOrderExecution"] - require.True(t, exist) - require.Equal(t, ooeDecoded, ooe) - - tokenReceiverDecoded, exist := m["tokenReceiver"] - require.True(t, exist) - require.Equal(t, tokenReceiverDecoded, tokenReceiver) - - accountsDecoded, exist := m["accounts"] - require.True(t, exist) - require.Equal(t, accountsDecoded, accounts) - }) } diff --git a/core/capabilities/ccip/ccipsolana/helpers.go b/core/capabilities/ccip/ccipsolana/extradatadecoder.go similarity index 100% rename from core/capabilities/ccip/ccipsolana/helpers.go rename to core/capabilities/ccip/ccipsolana/extradatadecoder.go diff --git a/core/capabilities/ccip/ccipsolana/helpers_test.go b/core/capabilities/ccip/ccipsolana/extradatadecoder_test.go similarity index 100% rename from core/capabilities/ccip/ccipsolana/helpers_test.go rename to core/capabilities/ccip/ccipsolana/extradatadecoder_test.go