Skip to content

Commit

Permalink
move sample functions ABIPack, CompactPack into memo pkg self; remove…
Browse files Browse the repository at this point in the history
… sample package reference from memo to minimize dependency
  • Loading branch information
ws4charlie committed Oct 15, 2024
1 parent 798f566 commit 4f5b1a1
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 240 deletions.
8 changes: 4 additions & 4 deletions pkg/memo/arg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package memo_test
import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"github.com/zeta-chain/node/pkg/memo"
"github.com/zeta-chain/node/testutil/sample"
)

func Test_NewArg(t *testing.T) {
argAddress := sample.EthAddress()
argString := sample.String()
argBytes := sample.Bytes()
argAddress := common.HexToAddress("0x0B85C56e5453e0f4273d1D1BF3091d43B08B38CE")
argString := "some other string argument"
argBytes := []byte("here is a bytes argument")

tests := []struct {
name string
Expand Down
131 changes: 122 additions & 9 deletions pkg/memo/codec_abi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,127 @@ package memo_test

import (
"bytes"
"encoding/binary"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"github.com/zeta-chain/node/pkg/memo"
"github.com/zeta-chain/node/testutil/sample"
)

const (
// abiAlignment is the number of bytes used to align the ABI encoded data
abiAlignment = 32
)

// ABIPack is a helper function that simulates the abi.Pack function.
// Note: all arguments are assumed to be <= 32 bytes for simplicity.
func ABIPack(t *testing.T, args ...memo.CodecArg) []byte {
packedData := make([]byte, 0)

// data offset for 1st dynamic-length field
offset := abiAlignment * len(args)

// 1. pack 32-byte offset for each dynamic-length field (bytes, string)
// 2. pack actual data for each fixed-length field (address)
for _, arg := range args {
switch arg.Type {
case memo.ArgTypeBytes:
// left-pad length as uint16
buff := make([]byte, 2)
binary.BigEndian.PutUint16(buff, uint16(offset))
offsetData := abiPad32(t, buff, true)
packedData = append(packedData, offsetData...)

argLen := len(arg.Arg.([]byte))
if argLen > 0 {
offset += abiAlignment * 2 // [length + data]
} else {
offset += abiAlignment // only [length]
}

case memo.ArgTypeString:
// left-pad length as uint16
buff := make([]byte, 2)
binary.BigEndian.PutUint16(buff, uint16(offset))
offsetData := abiPad32(t, buff, true)
packedData = append(packedData, offsetData...)

argLen := len([]byte(arg.Arg.(string)))
if argLen > 0 {
offset += abiAlignment * 2 // [length + data]
} else {
offset += abiAlignment // only [length]
}

case memo.ArgTypeAddress: // left-pad for address
data := abiPad32(t, arg.Arg.(common.Address).Bytes(), true)
packedData = append(packedData, data...)
}
}

// pack dynamic-length fields
dynamicData := abiPackDynamicData(t, args...)
packedData = append(packedData, dynamicData...)

return packedData
}

// abiPad32 is a helper function to pad a byte slice to 32 bytes
func abiPad32(t *testing.T, data []byte, left bool) []byte {
// nothing needs to be encoded, return empty bytes
if len(data) == 0 {
return []byte{}
}

require.LessOrEqual(t, len(data), abiAlignment)
padded := make([]byte, 32)

if left {
// left-pad the data for fixed-size types
copy(padded[32-len(data):], data)
} else {
// right-pad the data for dynamic types
copy(padded, data)
}
return padded
}

// abiPackDynamicData is a helper function to pack dynamic-length data
func abiPackDynamicData(t *testing.T, args ...memo.CodecArg) []byte {
packedData := make([]byte, 0)

// pack with ABI format: length + data
for _, arg := range args {
// get length
var length int
switch arg.Type {
case memo.ArgTypeBytes:
length = len(arg.Arg.([]byte))
case memo.ArgTypeString:
length = len([]byte(arg.Arg.(string)))
default:
continue
}

// append length in bytes
lengthData := abiPad32(t, []byte{byte(length)}, true)
packedData = append(packedData, lengthData...)

// append actual data in bytes
switch arg.Type {
case memo.ArgTypeBytes: // right-pad for bytes
data := abiPad32(t, arg.Arg.([]byte), false)
packedData = append(packedData, data...)
case memo.ArgTypeString: // right-pad for string
data := abiPad32(t, []byte(arg.Arg.(string)), false)
packedData = append(packedData, data...)
}
}

return packedData
}

// newArgInstance creates a new instance of the given argument type
func newArgInstance(v interface{}) interface{} {
switch v.(type) {
Expand Down Expand Up @@ -46,7 +159,7 @@ func Test_CodecABI_AddArguments(t *testing.T) {
codec := memo.NewCodecABI()
require.NotNil(t, codec)

address := sample.EthAddress()
address := common.HexToAddress("0xEf221eC80f004E6A2ee4E5F5d800699c1C68cD6F")
codec.AddArguments(memo.ArgReceiver(&address))

// attempt to pack the arguments, result should not be nil
Expand All @@ -57,7 +170,7 @@ func Test_CodecABI_AddArguments(t *testing.T) {

func Test_CodecABI_PackArgument(t *testing.T) {
// create sample arguments
argAddress := sample.EthAddress()
argAddress := common.HexToAddress("0xEf221eC80f004E6A2ee4E5F5d800699c1C68cD6F")
argBytes := []byte("some test bytes argument")
argString := "some test string argument"

Expand Down Expand Up @@ -124,7 +237,7 @@ func Test_CodecABI_PackArgument(t *testing.T) {
require.NoError(t, err)

// calc expected data for comparison
expectedData := sample.ABIPack(t, tc.args...)
expectedData := ABIPack(t, tc.args...)

// validate the packed data
require.True(t, bytes.Equal(expectedData, packedData), "ABI encoded data mismatch")
Expand All @@ -134,7 +247,7 @@ func Test_CodecABI_PackArgument(t *testing.T) {

func Test_CodecABI_UnpackArguments(t *testing.T) {
// create sample arguments
argAddress := sample.EthAddress()
argAddress := common.HexToAddress("0xEf221eC80f004E6A2ee4E5F5d800699c1C68cD6F")
argBytes := []byte("some test bytes argument")
argString := "some test string argument"

Expand All @@ -147,7 +260,7 @@ func Test_CodecABI_UnpackArguments(t *testing.T) {
}{
{
name: "unpack in the order of [address, bytes, string]",
data: sample.ABIPack(t,
data: ABIPack(t,
memo.ArgReceiver(argAddress),
memo.ArgPayload(argBytes),
memo.ArgRevertAddress(argString)),
Expand All @@ -159,7 +272,7 @@ func Test_CodecABI_UnpackArguments(t *testing.T) {
},
{
name: "unpack in the order of [string, address, bytes]",
data: sample.ABIPack(t,
data: ABIPack(t,
memo.ArgRevertAddress(argString),
memo.ArgReceiver(argAddress),
memo.ArgPayload(argBytes)),
Expand All @@ -171,7 +284,7 @@ func Test_CodecABI_UnpackArguments(t *testing.T) {
},
{
name: "unpack empty bytes array and string",
data: sample.ABIPack(t,
data: ABIPack(t,
memo.ArgPayload([]byte{}),
memo.ArgRevertAddress("")),
expected: []memo.CodecArg{
Expand All @@ -190,7 +303,7 @@ func Test_CodecABI_UnpackArguments(t *testing.T) {
},
{
name: "unpacking should fail on argument type mismatch",
data: sample.ABIPack(t,
data: ABIPack(t,
memo.ArgReceiver(argAddress),
),
expected: []memo.CodecArg{
Expand Down
Loading

0 comments on commit 4f5b1a1

Please sign in to comment.