Skip to content

Commit

Permalink
Teleporter CLI (#137)
Browse files Browse the repository at this point in the history
* init decoder

* initial decoder

* initial decoder

* decoding transaction Teleporter events

* cleaning up comments

* add documentation

* update licensing

* refactor event filtering into teleportermessenger package

* add abi bindings event

* update logging

* cleanup docs

* log tele/warp message id

* update go work sum

* extra pre run func and nits

* fix typo

* restructure cli directory

* fix linting for go

* add event unit test

* add unit tests for event and packing

* initial root and event tests

* getting event test to working state

* basic unit test and ci update

* update test step name

* remove extra logs

* update workflow name
  • Loading branch information
minghinmatthewlam authored Dec 5, 2023
1 parent bf805e5 commit 93e3077
Show file tree
Hide file tree
Showing 21 changed files with 857 additions and 39 deletions.
26 changes: 0 additions & 26 deletions .github/workflows/abi_bindings_checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,3 @@ jobs:

- name: Fail if diff exists
run: git --no-pager diff --quiet -- abi-bindings/**.go

unit_tests:
name: Unit tests
runs-on: ubuntu-20.04

steps:
- name: Checkout repositories and submodules
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set Go version
run: |
source ./scripts/versions.sh
GO_VERSION=$GO_VERSION >> $GITHUB_ENV
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}

- name: Run ABI Binding Unit Tests
run: |
source scripts/constants.sh
cd abi-bindings/go
go test ./...
28 changes: 26 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: E2E and Solidity Unit Tests
name: Tests

on:
push:
Expand Down Expand Up @@ -26,7 +26,31 @@ jobs:
export PATH=$PATH:$HOME/.foundry/bin
cd contracts/
forge test -vvv
go-unit-tests:
runs-on: ubuntu-20.04

steps:
- name: Checkout repositories and submodules
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set Go version
run: |
source ./scripts/versions.sh
GO_VERSION=$GO_VERSION >> $GITHUB_ENV
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}

- name: Run Go unit tests
run: |
source scripts/constants.sh
go test ./...
e2e_tests:
name: e2e_tests
runs-on: ubuntu-20.04
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Cargo.lock
# IDE configurations
.vscode*

# Binaries
cmd/teleporter-cli/teleporter-cli

# File generated by tests
relayerConfig.json
subnetGenesis_*
Expand Down
101 changes: 101 additions & 0 deletions abi-bindings/go/Teleporter/TeleporterMessenger/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package teleportermessenger

import (
"fmt"
"strings"

"github.com/ethereum/go-ethereum/common"
)

// Event is a Teleporter log event
type Event uint8

const (
Unknown Event = iota
SendCrossChainMessage
ReceiveCrossChainMessage
AddFeeAmount
MessageExecutionFailed
MessageExecuted
RelayerRewardsRedeemed

sendCrossChainMessageStr = "SendCrossChainMessage"
receiveCrossChainMessageStr = "ReceiveCrossChainMessage"
addFeeAmountStr = "AddFeeAmount"
messageExecutionFailedStr = "MessageExecutionFailed"
messageExecutedStr = "MessageExecuted"
relayerRewardsRedeemedStr = "RelayerRewardsRedeemed"
unknownStr = "Unknown"
)

// String returns the string representation of an Event
func (e Event) String() string {
switch e {
case SendCrossChainMessage:
return sendCrossChainMessageStr
case ReceiveCrossChainMessage:
return receiveCrossChainMessageStr
case AddFeeAmount:
return addFeeAmountStr
case MessageExecutionFailed:
return messageExecutionFailedStr
case MessageExecuted:
return messageExecutedStr
case RelayerRewardsRedeemed:
return relayerRewardsRedeemedStr
default:
return unknownStr
}
}

// ToEvent converts a string to an Event
func ToEvent(e string) (Event, error) {
switch strings.ToLower(e) {
case strings.ToLower(sendCrossChainMessageStr):
return SendCrossChainMessage, nil
case strings.ToLower(receiveCrossChainMessageStr):
return ReceiveCrossChainMessage, nil
case strings.ToLower(addFeeAmountStr):
return AddFeeAmount, nil
case strings.ToLower(messageExecutionFailedStr):
return MessageExecutionFailed, nil
case strings.ToLower(messageExecutedStr):
return MessageExecuted, nil
case strings.ToLower(relayerRewardsRedeemedStr):
return RelayerRewardsRedeemed, nil
default:
return Unknown, fmt.Errorf("unknown event %s", e)
}
}

// FilterTeleporterEvents parses the topics and data of a Teleporter log into the corresponding Teleporter event
func FilterTeleporterEvents(topics []common.Hash, data []byte, event string) (interface{}, error) {
e, err := ToEvent(event)
if err != nil {
return nil, err
}
var out interface{}
switch e {
case SendCrossChainMessage:
out = new(TeleporterMessengerSendCrossChainMessage)
case ReceiveCrossChainMessage:
out = new(TeleporterMessengerReceiveCrossChainMessage)
case AddFeeAmount:
out = new(TeleporterMessengerAddFeeAmount)
case MessageExecutionFailed:
out = new(TeleporterMessengerMessageExecutionFailed)
case MessageExecuted:
out = new(TeleporterMessengerMessageExecuted)
case RelayerRewardsRedeemed:
out = new(TeleporterMessengerRelayerRewardsRedeemed)
default:
return nil, fmt.Errorf("unknown event %s", e.String())
}
if err := UnpackEvent(out, e.String(), topics, data); err != nil {
return nil, err
}
return out, nil
}
143 changes: 143 additions & 0 deletions abi-bindings/go/Teleporter/TeleporterMessenger/event_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package teleportermessenger

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)

func TestEventString(t *testing.T) {
var (
tests = []struct {
event Event
str string
}{
{Unknown, unknownStr},
{SendCrossChainMessage, sendCrossChainMessageStr},
{ReceiveCrossChainMessage, receiveCrossChainMessageStr},
{AddFeeAmount, addFeeAmountStr},
{MessageExecutionFailed, messageExecutionFailedStr},
{MessageExecuted, messageExecutedStr},
{RelayerRewardsRedeemed, relayerRewardsRedeemedStr},
}
)

for _, test := range tests {
t.Run(test.event.String(), func(t *testing.T) {
require.Equal(t, test.event.String(), test.str)
})
}
}

func TestToEvent(t *testing.T) {
var (
tests = []struct {
str string
event Event
isError bool
}{
{unknownStr, Unknown, true},
{sendCrossChainMessageStr, SendCrossChainMessage, false},
{receiveCrossChainMessageStr, ReceiveCrossChainMessage, false},
{addFeeAmountStr, AddFeeAmount, false},
{messageExecutionFailedStr, MessageExecutionFailed, false},
{messageExecutedStr, MessageExecuted, false},
{relayerRewardsRedeemedStr, RelayerRewardsRedeemed, false},
}
)

for _, test := range tests {
t.Run(test.str, func(t *testing.T) {
event, err := ToEvent(test.str)
if test.isError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
require.Equal(t, test.event, event)
})
}
}

func TestFilterTeleporterEvents(t *testing.T) {
mockBlockchainID := [32]byte{1, 2, 3, 4}
messageID := big.NewInt(1)
message := createTestTeleporterMessage(messageID.Int64())
feeInfo := TeleporterFeeInfo{
FeeTokenAddress: common.HexToAddress("0x0123456789abcdef0123456789abcdef01234567"),
Amount: big.NewInt(1),
}
deliverer := common.HexToAddress("0x0123456789abcdef0123456789abcdef01234567")

teleporterABI, err := TeleporterMessengerMetaData.GetAbi()
require.NoError(t, err)

var (
tests = []struct {
event Event
args []interface{}
expected interface{}
}{
{
event: SendCrossChainMessage,
args: []interface{}{
mockBlockchainID,
messageID,
message,
feeInfo,
},
expected: &TeleporterMessengerSendCrossChainMessage{
DestinationBlockchainID: mockBlockchainID,
MessageID: messageID,
Message: message,
FeeInfo: feeInfo,
},
},
{
event: ReceiveCrossChainMessage,
args: []interface{}{
mockBlockchainID,
messageID,
deliverer,
deliverer,
message,
},
expected: &TeleporterMessengerReceiveCrossChainMessage{
OriginBlockchainID: mockBlockchainID,
MessageID: messageID,
Deliverer: deliverer,
RewardRedeemer: deliverer,
Message: message,
},
},
{
event: MessageExecuted,
args: []interface{}{
mockBlockchainID,
messageID,
},
expected: &TeleporterMessengerMessageExecuted{
OriginBlockchainID: mockBlockchainID,
MessageID: messageID,
},
},
}
)

for _, test := range tests {
t.Run(test.event.String(), func(t *testing.T) {
topics, data, err := teleporterABI.PackEvent(test.event.String(), test.args...)
require.NoError(t, err)

out, err := FilterTeleporterEvents(topics, data, test.event.String())
require.NoError(t, err)

require.Equal(t, test.expected, out)
})
}
}
21 changes: 21 additions & 0 deletions abi-bindings/go/Teleporter/TeleporterMessenger/packing.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,24 @@ func PackMessageReceivedOutput(success bool) ([]byte, error) {

return abi.PackOutput("messageReceived", success)
}

// UnpackEvent unpacks the event data and topics into the provided interface
func UnpackEvent(out interface{}, event string, topics []common.Hash, data []byte) error {
teleporterABI, err := TeleporterMessengerMetaData.GetAbi()
if err != nil {
return fmt.Errorf("failed to get abi: %v", err)
}
if len(data) > 0 {
if err := teleporterABI.UnpackIntoInterface(out, event, data); err != nil {
return err
}
}

var indexed abi.Arguments
for _, arg := range teleporterABI.Events[event].Inputs {
if arg.Indexed {
indexed = append(indexed, arg)
}
}
return abi.ParseTopics(out, indexed, topics[1:])
}
Loading

0 comments on commit 93e3077

Please sign in to comment.