Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: standard non-evm inbound memo #2987

Merged
merged 23 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6e7fe1a
initiated memo package
ws4charlie Oct 8, 2024
95acf7c
add unit tests against memo fields version 0
ws4charlie Oct 9, 2024
bde439f
added memo unit tests
ws4charlie Oct 9, 2024
9272a2a
use separate file for memo header; add more unit tests
ws4charlie Oct 10, 2024
a1f3da8
a few renaming and wrapped err message
ws4charlie Oct 10, 2024
cb72262
add extra good-to-have check for memo fields validation
ws4charlie Oct 10, 2024
523f6df
Merge branch 'develop' into feat-inbound-memo
ws4charlie Oct 10, 2024
ed34ac4
add changelog entry
ws4charlie Oct 10, 2024
416b8d4
fix nosec error
ws4charlie Oct 10, 2024
c48d999
add two more unit tests for missed lines
ws4charlie Oct 10, 2024
9f92e23
remove redundant dependency github.com/test-go/testify v1.1.4
ws4charlie Oct 10, 2024
6021f98
enhance codec error message
ws4charlie Oct 10, 2024
fc13377
a few renaming and move test constant to testutil pkg
ws4charlie Oct 10, 2024
5b59e19
corrected typo and improved unit tests
ws4charlie Oct 10, 2024
6f141eb
Merge branch 'develop' into feat-inbound-memo
ws4charlie Oct 10, 2024
b3fabbd
fix build
ws4charlie Oct 10, 2024
dd80da8
make receiver address optional
ws4charlie Oct 11, 2024
d022be1
move bits.go to bits folder; type defines for OpCode and EncodingForm…
ws4charlie Oct 15, 2024
9bee20b
Merge branch 'develop' into feat-inbound-memo
ws4charlie Oct 15, 2024
798f566
move legacy Bitcoin memo decoding to memo package
ws4charlie Oct 15, 2024
4f5b1a1
move sample functions ABIPack, CompactPack into memo pkg self; remove…
ws4charlie Oct 15, 2024
b3426af
fix unit test compile error
ws4charlie Oct 16, 2024
49ef1d8
Merge branch 'develop' into feat-inbound-memo
ws4charlie Oct 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* [2919](https://github.com/zeta-chain/node/pull/2919) - add inbound sender to revert context
* [2957](https://github.com/zeta-chain/node/pull/2957) - enable Bitcoin inscription support on testnet
* [2896](https://github.com/zeta-chain/node/pull/2896) - add TON inbound observation
* [2987](https://github.com/zeta-chain/node/pull/2987) - add non-EVM standard inbound memo package

### Refactor

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ require (
require (
github.com/bnb-chain/tss-lib v1.5.0
github.com/showa-93/go-mask v0.6.2
github.com/test-go/testify v1.1.4
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
github.com/tonkeeper/tongo v1.9.3
)

Expand Down
52 changes: 52 additions & 0 deletions pkg/math/bits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package math
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved

import (
"math/bits"
)

// SetBit sets the bit at the given position (0-7) in the byte to 1
func SetBit(b *byte, position uint8) {
if position > 7 {
return
}
*b |= 1 << position
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
}

// IsBitSet returns true if the bit at the given position (0-7) is set in the byte, false otherwise
func IsBitSet(b byte, position uint8) bool {
if position > 7 {
return false
}
bitMask := byte(1 << position)
return b&bitMask != 0
}

// GetBits extracts the value of bits for a given mask
//
// Example: given b = 0b11011001 and mask = 0b11100000, the function returns 0b110
func GetBits(b byte, mask byte) byte {
extracted := b & mask

// get the number of trailing zero bits
trailingZeros := bits.TrailingZeros8(mask)

// remove trailing zeros
return extracted >> trailingZeros
}

// SetBits sets the value to the bits specified in the mask
//
// Example: given b = 0b00100001 and mask = 0b11100000, and value = 0b110, the function returns 0b11000001
func SetBits(b byte, mask byte, value byte) byte {
// get the number of trailing zero bits in the mask
trailingZeros := bits.TrailingZeros8(mask)

// shift the value left by the number of trailing zeros
valueShifted := value << trailingZeros

// clear the bits in 'b' that correspond to the mask
bCleared := b &^ mask

// Set the bits by ORing the cleared 'b' with the shifted value
return bCleared | valueShifted
}
171 changes: 171 additions & 0 deletions pkg/math/bits_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package math_test

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/zeta-chain/node/pkg/math"
)

func TestSetBit(t *testing.T) {
tests := []struct {
name string
initial byte
position uint8
expected byte
}{
{
name: "set bit at position 0",
initial: 0b00001000,
position: 0,
expected: 0b00001001,
},
{
name: "set bit at position 7",
initial: 0b00001000,
position: 7,
expected: 0b10001000,
},
{
name: "out of range bit position (no effect)",
initial: 0b00000000,
position: 8, // Out of range
expected: 0b00000000,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := tt.initial
math.SetBit(&b, tt.position)
require.Equal(t, tt.expected, b)
})
}
}

func TestIsBitSet(t *testing.T) {
tests := []struct {
name string
b byte
position uint8
expected bool
}{
{
name: "bit 0 set",
b: 0b00000001,
position: 0,
expected: true,
},
{
name: "bit 7 set",
b: 0b10000000,
position: 7,
expected: true,
},
{
name: "bit 2 not set",
b: 0b00000001,
position: 2,
expected: false,
},
{
name: "bit out of range",
b: 0b00000001,
position: 8, // Position out of range
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := math.IsBitSet(tt.b, tt.position)
require.Equal(t, tt.expected, result)
})
}
}

func TestGetBits(t *testing.T) {
tests := []struct {
name string
b byte
mask byte
expected byte
}{
{
name: "extract upper 3 bits",
b: 0b11011001,
mask: 0b11100000,
expected: 0b110,
},
{
name: "extract middle 3 bits",
b: 0b11011001,
mask: 0b00011100,
expected: 0b110,
},
{
name: "extract lower 3 bits",
b: 0b11011001,
mask: 0b00000111,
expected: 0b001,
},
{
name: "extract no bits",
b: 0b11011001,
mask: 0b00000000,
expected: 0b000,
},
{
name: "extract all bits",
b: 0b11111111,
mask: 0b11111111,
expected: 0b11111111,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := math.GetBits(tt.b, tt.mask)
require.Equal(t, tt.expected, result)
})
}
}

func TestSetBits(t *testing.T) {
tests := []struct {
name string
b byte
mask byte
value byte
expected byte
}{
{
name: "set upper 3 bits",
b: 0b00100001,
mask: 0b11100000,
value: 0b110,
expected: 0b11000001,
},
{
name: "set middle 3 bits",
b: 0b00100001,
mask: 0b00011100,
value: 0b101,
expected: 0b00110101,
},
{
name: "set lower 3 bits",
b: 0b11111100,
mask: 0b00000111,
value: 0b101,
expected: 0b11111101,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := math.SetBits(tt.b, tt.mask, tt.value)
require.Equal(t, tt.expected, result)
})
}
}
52 changes: 52 additions & 0 deletions pkg/memo/arg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package memo

// ArgType is the enum for types supported by the codec
type ArgType string

// Define all the types supported by the codec
const (
ArgTypeBytes ArgType = "bytes"
ArgTypeString ArgType = "string"
ArgTypeAddress ArgType = "address"
)
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved

// CodecArg represents a codec argument
type CodecArg struct {
Name string
Type ArgType
Arg interface{}
}
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved

// NewArg create a new codec argument
func NewArg(name string, argType ArgType, arg interface{}) CodecArg {
return CodecArg{
Name: name,
Type: argType,
Arg: arg,
}
}

// ArgReceiver wraps the receiver address in a CodecArg
func ArgReceiver(arg interface{}) CodecArg {
return NewArg("receiver", ArgTypeAddress, arg)
}

// ArgPayload wraps the payload in a CodecArg
func ArgPayload(arg interface{}) CodecArg {
return NewArg("payload", ArgTypeBytes, arg)
}

// ArgRevertAddress wraps the revert address in a CodecArg
func ArgRevertAddress(arg interface{}) CodecArg {
return NewArg("revertAddress", ArgTypeString, arg)
}

// ArgAbortAddress wraps the abort address in a CodecArg
func ArgAbortAddress(arg interface{}) CodecArg {
return NewArg("abortAddress", ArgTypeAddress, arg)
}

// ArgRevertMessage wraps the revert message in a CodecArg
func ArgRevertMessage(arg interface{}) CodecArg {
return NewArg("revertMessage", ArgTypeBytes, arg)
}
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
70 changes: 70 additions & 0 deletions pkg/memo/arg_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package memo_test

import (
"testing"

"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()

tests := []struct {
name string
argType string
arg interface{}
}{
{
name: "receiver",
argType: "address",
arg: &argAddress,
},
{
name: "payload",
argType: "bytes",
arg: &argBytes,
},
{
name: "revertAddress",
argType: "string",
arg: &argString,
},
{
name: "abortAddress",
argType: "address",
arg: &argAddress,
},
{
name: "revertMessage",
argType: "bytes",
arg: &argBytes,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
arg := memo.NewArg(tt.name, memo.ArgType(tt.argType), tt.arg)

require.Equal(t, tt.name, arg.Name)
require.Equal(t, memo.ArgType(tt.argType), arg.Type)
require.Equal(t, tt.arg, arg.Arg)

switch tt.name {
case "receiver":
require.Equal(t, arg, memo.ArgReceiver(&argAddress))
case "payload":
require.Equal(t, arg, memo.ArgPayload(&argBytes))
case "revertAddress":
require.Equal(t, arg, memo.ArgRevertAddress(&argString))
case "abortAddress":
require.Equal(t, arg, memo.ArgAbortAddress(&argAddress))
case "revertMessage":
require.Equal(t, arg, memo.ArgRevertMessage(&argBytes))
}
})
}
}
Loading
Loading