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

Teleporter CLI #137

Merged
merged 34 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
45ec606
init decoder
Nov 10, 2023
48c1401
initial decoder
Nov 10, 2023
7be72de
initial decoder
Nov 13, 2023
1fc795c
Merge remote-tracking branch 'origin/main' into decoder
Nov 14, 2023
3260392
decoding transaction Teleporter events
Nov 14, 2023
3f80609
cleaning up comments
Nov 14, 2023
3e12079
add documentation
Nov 15, 2023
6b6fee8
update licensing
Nov 15, 2023
ea0e8d6
Merge remote-tracking branch 'origin/main' into decoder
Nov 15, 2023
ed05abf
refactor event filtering into teleportermessenger package
Nov 15, 2023
d68d809
add abi bindings event
Nov 15, 2023
c833384
update logging
Nov 15, 2023
277d497
cleanup docs
Nov 15, 2023
f498dfe
Merge branch 'main' into decoder
Nov 15, 2023
bdaff96
log tele/warp message id
Nov 16, 2023
0b9ae75
update go work sum
Nov 22, 2023
aad2764
Merge remote-tracking branch 'origin/main' into decoder
Nov 22, 2023
e57ec79
Merge remote-tracking branch 'origin/main' into decoder
Nov 29, 2023
e80a174
extra pre run func and nits
Nov 29, 2023
5c84036
fix typo
Nov 29, 2023
2d0b810
restructure cli directory
Nov 29, 2023
1682fd3
Merge remote-tracking branch 'origin/main' into decoder
Nov 29, 2023
0c3685d
fix linting for go
Nov 29, 2023
42898b4
add event unit test
Nov 29, 2023
7ae5094
add unit tests for event and packing
Nov 29, 2023
fb71ca9
initial root and event tests
Nov 30, 2023
1b24a8d
getting event test to working state
Dec 1, 2023
dd3a74b
basic unit test and ci update
Dec 4, 2023
1cdb979
Merge remote-tracking branch 'origin/main' into decoder
Dec 4, 2023
a4aa24d
update test step name
Dec 4, 2023
bc3e528
Merge remote-tracking branch 'origin/main' into decoder
Dec 4, 2023
c9d2a97
Merge remote-tracking branch 'origin/main' into decoder
Dec 5, 2023
96215f9
remove extra logs
Dec 5, 2023
df5d4d9
update workflow name
Dec 5, 2023
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ cache/
target/
Cargo.lock

# Example binary
# Binaries
blst_example
cli/teleporter-cli

# File generated by tests
relayerConfig.json
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", event)
}
if err := UnpackEvent(out, event, topics, data); err != nil {
return nil, err
}
return out, nil
}
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 @@ -115,3 +115,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:])
}
17 changes: 17 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Teleporter CLI

This directory contains the source code for the Teleporter CLI. The CLI is a command line interface for interacting with the Teleporter contracts. It is written with [cobra](https://github.com/spf13/cobra) commands as a Go application.

## Build

To build the CLI, run `go build -o teleporter-cli` from this directory. This will create a binary called `teleporter-cli` in the current directory.
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved

## Usage

The CLI has a number of subcommands. To see the list of subcommands, run `./teleporter-cli help`. To see the help for a specific subcommand, run `./teleporter-cli help <subcommand>`.

The supported subcommands include:

- `event`: given a log event's topics and data, attempts to decode into a Teleporter event in more readable format.
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
- `message`: given a Teleporter message bytes hex encoded, attempts to decode into a Teleporter message in more readable format.
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
- `transaction`: given a transaction hash, attempts to decode all relevant Teleporter and Warp log events in more readable format.
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
47 changes: 47 additions & 0 deletions cli/cmd/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package cmd

import (
teleportermessenger "github.com/ava-labs/teleporter/abi-bindings/go/Teleporter/TeleporterMessenger"
"github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"
"go.uber.org/zap"
)

var (
topicArgs []string
data []byte
)

var eventCmd = &cobra.Command{
Use: "event --topics topic1, topic2",
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
Short: "Parses a Teleporter log's topics and data",
Long: `Given the topics and data of a Teleporter log, parses the log into
the corresponding Teleporter event. Topics are represented by a hash,
and data is the hex encoding of the bytes.`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
var topics []common.Hash
for _, topic := range topicArgs {
topics = append(topics, common.HexToHash(topic))
}

event, err := teleporterABI.EventByID(topics[0])
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
cobra.CheckErr(err)

out, err := teleportermessenger.FilterTeleporterEvents(topics, data, event.Name)
cobra.CheckErr(err)
logger.Info("Parsed Teleporter event", zap.String("name", event.Name), zap.Any("event", out))
},
}

func init() {
rootCmd.AddCommand(eventCmd)
eventCmd.PersistentFlags().StringSliceVar(&topicArgs, "topics", []string{}, "Topic hashes of the event")
eventCmd.Flags().BytesHexVar(&data, "data", []byte{}, "Hex encoded data of the event")

err := eventCmd.MarkPersistentFlagRequired("topics")
cobra.CheckErr(err)
}
33 changes: 33 additions & 0 deletions cli/cmd/message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package cmd

import (
"encoding/hex"

teleportermessenger "github.com/ava-labs/teleporter/abi-bindings/go/Teleporter/TeleporterMessenger"
"github.com/spf13/cobra"
"go.uber.org/zap"
)

var messageCmd = &cobra.Command{
Use: "message MESSAGE_BYTES",
Short: "Decodes hex encoded Teleporter message bytes into a TeleporterMessage struct",
Long: `Given the hex encoded bytes of a Teleporter message, this command will decode
the bytes into a TeleporterMessage struct and print the struct fields.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
encodedMsg := args[0]
b, err := hex.DecodeString(encodedMsg)
cobra.CheckErr(err)

msg, err := teleportermessenger.UnpackTeleporterMessage(b)
cobra.CheckErr(err)
logger.Info("Teleporter Message unpacked", zap.Any("message", msg))
},
}

func init() {
rootCmd.AddCommand(messageCmd)
}
64 changes: 64 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package cmd

import (
"os"

"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/subnet-evm/accounts/abi"
teleportermessenger "github.com/ava-labs/teleporter/abi-bindings/go/Teleporter/TeleporterMessenger"
"github.com/spf13/cobra"
)

var (
logger logging.Logger
teleporterABI *abi.ABI
)

var rootCmd = &cobra.Command{
Use: "teleporter-cli",
Short: "A CLI that integrates with the Teleporter protocol",
Long: `A CLI that integrates with the Teleporter protocol, and allows you
to debug Teleporter on chain activity. The CLI can help decode
Teleporter and Warp events, as well as parsing Teleporter messages.`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
geoff-vball marked this conversation as resolved.
Show resolved Hide resolved
}
}

func init() {
rootCmd.CompletionOptions.DisableDefaultCmd = true
logLevelArg := rootCmd.PersistentFlags().StringP("log", "l", "", "Log level i.e. debug, info...")
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if *logLevelArg == "" {
*logLevelArg = logging.Info.LowerString()
}

logLevel, err := logging.ToLevel(*logLevelArg)
if err != nil {
return err
}
logger = logging.NewLogger(
"teleporter-cli",
logging.NewWrappedCore(
logLevel,
os.Stdout,
logging.Plain.ConsoleEncoder(),
),
)
abi, err := teleportermessenger.TeleporterMessengerMetaData.GetAbi()
if err != nil {
return err
}
teleporterABI = abi
return nil
}
}
94 changes: 94 additions & 0 deletions cli/cmd/transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package cmd

import (
"context"

warpPayload "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload"
"github.com/ava-labs/subnet-evm/ethclient"
"github.com/ava-labs/subnet-evm/x/warp"
teleportermessenger "github.com/ava-labs/teleporter/abi-bindings/go/Teleporter/TeleporterMessenger"
"github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"
"go.uber.org/zap"
)

const (
warpPrecompileAddress = "0x0200000000000000000000000000000000000005"
)

var (
rpcEndpoint string
contractAddress common.Address
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
client ethclient.Client
)

var transactionCmd = &cobra.Command{
Use: "transaction --rpc RPC_URL --contract-address CONTRACT_ADDRESS TRANSACTION_HASH",
Short: "Parses relevant Teleporter logs from a transaction",
Long: `Given a transaction this command looks through the transaction's receipt
for Teleporter and Warp log events. When corresponding log events are found,
the command parses to log event fields to a more human readable format.`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
receipt, err := client.TransactionReceipt(context.Background(),
common.HexToHash(args[0]))
cobra.CheckErr(err)
for _, log := range receipt.Logs {
if log.Address == contractAddress {
logger.Debug("Processing Teleporter log", zap.Any("log", log))

event, err := teleporterABI.EventByID(log.Topics[0])
cobra.CheckErr(err)

out, err := teleportermessenger.FilterTeleporterEvents(log.Topics, log.Data, event.Name)
cobra.CheckErr(err)
logger.Info("Parsed Teleporter event", zap.String("name", event.Name), zap.Any("event", out))
}

if log.Address == common.HexToAddress(warpPrecompileAddress) {
logger.Debug("Processing Warp log", zap.Any("log", log))

unsignedMsg, err := warp.UnpackSendWarpEventDataToMessage(log.Data)
cobra.CheckErr(err)

warpPayload, err := warpPayload.ParseAddressedCall(unsignedMsg.Payload)
cobra.CheckErr(err)

teleporterMessage, err := teleportermessenger.UnpackTeleporterMessage(warpPayload.Payload)
cobra.CheckErr(err)
logger.Info("Parsed Teleporter message", zap.String("warpMessageID", unsignedMsg.ID().Hex()), zap.String("teleporterMessageID", teleporterMessage.MessageID.String()), zap.Any("message", teleporterMessage))
}

}

},
}

func init() {
rootCmd.AddCommand(transactionCmd)
transactionCmd.PersistentFlags().StringVar(&rpcEndpoint, "rpc", "", "RPC endpoint to connect to the node")
address := transactionCmd.PersistentFlags().StringP("contract-address", "c", "", "Teleporter contract address")
err := transactionCmd.MarkPersistentFlagRequired("rpc")
cobra.CheckErr(err)
err = transactionCmd.MarkPersistentFlagRequired("contract-address")
cobra.CheckErr(err)
transactionCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
// Run the persistent pre-run function of the root command if it exists.
if rootCmd.PersistentPreRunE != nil {
if err := rootCmd.PersistentPreRunE(cmd, args); err != nil {
return err
}
}
minghinmatthewlam marked this conversation as resolved.
Show resolved Hide resolved
contractAddress = common.HexToAddress(*address)
c, err := ethclient.Dial(rpcEndpoint)
if err != nil {
return err
}

client = c
return err
}
}
Loading
Loading