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

Update docker image and substrate event field handling #143

Merged
merged 2 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2020 ChainSafe Systems
# SPDX-License-Identifier: LGPL-3.0-only

FROM golang:1.18-stretch AS builder
FROM golang:1.22 AS builder
ADD . /src
WORKDIR /src
RUN go mod download
Expand Down
17 changes: 12 additions & 5 deletions chains/substrate/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ The current supported transfer types are Fungible, Nonfungible, and generic.

There are 3 major components: the connection, the listener, and the writer.

Connection
# Connection

The Connection handles connecting to the substrate client, and submitting transactions to the client.
It also handles state queries. The connection is shared by the writer and listener.

Listener
# Listener

The substrate listener polls blocks and parses the associated events for the three transfer types. It then forwards these into the router.

Writer
# Writer

As the writer receives messages from the router, it constructs proposals. If a proposal is still active, the writer will attempt to vote on it. Resource IDs are resolved to method name on-chain, which are then used in the proposals when constructing the resulting Call struct.

*/
package substrate

Expand All @@ -32,8 +31,10 @@ import (
"github.com/centrifuge/chainbridge-utils/keystore"
metrics "github.com/centrifuge/chainbridge-utils/metrics/types"
"github.com/centrifuge/chainbridge-utils/msg"
"github.com/centrifuge/go-substrate-rpc-client/v4/registry"
"github.com/centrifuge/go-substrate-rpc-client/v4/registry/retriever"
"github.com/centrifuge/go-substrate-rpc-client/v4/registry/state"
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
)

var _ core.Chain = &Chain{}
Expand Down Expand Up @@ -105,7 +106,13 @@ func InitializeChain(cfg *core.ChainConfig, logger log15.Logger, sysErr chan<- e

ue := parseUseExtended(cfg)

eventRetriever, err := retriever.NewDefaultEventRetriever(state.NewEventProvider(conn.api.RPC.State), conn.api.RPC.State)
// u256 is represented as [u64;4]. We use this override to skip extra processing when decoding fields with this type.
u256FieldOverride := registry.FieldOverride{
FieldLookupIndex: 142,
FieldDecoder: &registry.ValueDecoder[types.U256]{},
}

eventRetriever, err := retriever.NewDefaultEventRetriever(state.NewEventProvider(conn.api.RPC.State), conn.api.RPC.State, u256FieldOverride)

if err != nil {
return nil, fmt.Errorf("event retriever creation: %w", err)
Expand Down
95 changes: 19 additions & 76 deletions chains/substrate/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ package substrate
import (
"errors"
"fmt"
"math/big"

"github.com/ChainSafe/log15"
"github.com/centrifuge/chainbridge-utils/msg"
"github.com/centrifuge/go-substrate-rpc-client/v4/registry"
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
"github.com/centrifuge/go-substrate-rpc-client/v4/types/codec"
)

type eventName string
type eventHandler func(map[string]any, log15.Logger) (msg.Message, error)
type eventHandler func(registry.DecodedFields, log15.Logger) (msg.Message, error)

const FungibleTransfer eventName = "ChainBridge.FungibleTransfer"
const NonFungibleTransfer eventName = "ChainBridge.NonFungibleTransfer"
Expand All @@ -30,7 +28,7 @@ var Subscriptions = []struct {
{GenericTransfer, genericTransferHandler},
}

func fungibleTransferHandler(eventFields map[string]any, log log15.Logger) (msg.Message, error) {
func fungibleTransferHandler(eventFields registry.DecodedFields, log log15.Logger) (msg.Message, error) {
chainID, err := getFieldValueAsType[types.U8]("ChainId", eventFields)
if err != nil {
return msg.Message{}, err
Expand All @@ -51,7 +49,7 @@ func fungibleTransferHandler(eventFields map[string]any, log log15.Logger) (msg.
return msg.Message{}, err
}

amount, err := getU256(eventFields)
amount, err := getFieldValueAsType[types.U256]("primitive_types.U256.U256", eventFields)
if err != nil {
return msg.Message{}, err
}
Expand All @@ -73,13 +71,13 @@ func fungibleTransferHandler(eventFields map[string]any, log log15.Logger) (msg.
), nil
}

func nonFungibleTransferHandler(_ map[string]any, log log15.Logger) (msg.Message, error) {
func nonFungibleTransferHandler(_ registry.DecodedFields, log log15.Logger) (msg.Message, error) {
log.Warn("Got non-fungible transfer event!")

return msg.Message{}, errors.New("non-fungible transfer not supported")
}

func genericTransferHandler(eventFields map[string]any, log log15.Logger) (msg.Message, error) {
func genericTransferHandler(eventFields registry.DecodedFields, log log15.Logger) (msg.Message, error) {
chainID, err := getFieldValueAsType[types.U8]("ChainId", eventFields)
if err != nil {
return msg.Message{}, err
Expand Down Expand Up @@ -130,26 +128,26 @@ func to32Bytes(array []types.U8) ([32]byte, error) {
return res, nil
}

func getFieldValueAsType[T any](fieldName string, eventFields map[string]any) (T, error) {
func getFieldValueAsType[T any](fieldName string, eventFields registry.DecodedFields) (T, error) {
var t T

for name, value := range eventFields {
if name == fieldName {
if v, ok := value.(T); ok {
for _, field := range eventFields {
if field.Name == fieldName {
if v, ok := field.Value.(T); ok {
return v, nil
}

return t, fmt.Errorf("field type mismatch, expected %T, got %T", t, value)
return t, fmt.Errorf("field type mismatch, expected %T, got %T", t, field.Value)
}
}

return t, fmt.Errorf("field with name '%s' not found", fieldName)
}

func getFieldValueAsSliceOfType[T any](fieldName string, eventFields map[string]any) ([]T, error) {
for name, value := range eventFields {
if name == fieldName {
value, ok := value.([]any)
func getFieldValueAsSliceOfType[T any](fieldName string, eventFields registry.DecodedFields) ([]T, error) {
for _, field := range eventFields {
if field.Name == fieldName {
value, ok := field.Value.([]any)

if !ok {
return nil, errors.New("field value not an array")
Expand All @@ -168,10 +166,10 @@ func getFieldValueAsSliceOfType[T any](fieldName string, eventFields map[string]
return nil, fmt.Errorf("field with name '%s' not found", fieldName)
}

func getFieldValueAsByteSlice(fieldName string, eventFields map[string]any) ([]byte, error) {
for name, value := range eventFields {
if name == fieldName {
value, ok := value.([]any)
func getFieldValueAsByteSlice(fieldName string, eventFields registry.DecodedFields) ([]byte, error) {
for _, field := range eventFields {
if field.Name == fieldName {
value, ok := field.Value.([]any)

if !ok {
return nil, errors.New("field value not an array")
Expand Down Expand Up @@ -222,58 +220,3 @@ func convertToByteSlice(array []types.U8) ([]byte, error) {

return res, nil
}

func getU256(eventFields map[string]any) (types.U256, error) {
for fieldName, fieldValue := range eventFields {
if fieldName != "primitive_types.U256.U256" {
continue
}

innerField, ok := fieldValue.(map[string]any)
if !ok {
return types.NewU256(*big.NewInt(0)), errors.New("unexpected amount field structure")
}

innerFieldVal, ok := innerField["[u64; 4]"]
if !ok {
return types.NewU256(*big.NewInt(0)), errors.New("amount field key not found")
}

slice, ok := innerFieldVal.([]any)
if !ok {
return types.NewU256(*big.NewInt(0)), errors.New("inner field value not a slice")
}

val, err := convertSliceToType[types.U64](slice)

if err != nil {
return types.NewU256(*big.NewInt(0)), err
}

if len(val) != 4 {
return types.NewU256(*big.NewInt(0)), errors.New("slice length mismatch")
}

var r [4]types.U64

for i, item := range val {
r[i] = item
}

encVal, err := codec.Encode(r)

if err != nil {
return types.NewU256(*big.NewInt(0)), errors.New("couldn't encode amount val")
}

var res types.U256

if err := codec.Decode(encVal, &res); err != nil {
return types.NewU256(*big.NewInt(0)), errors.New("couldn't decode amount")
}

return res, nil
}

return types.NewU256(*big.NewInt(0)), errors.New("amount field not found")
}
Loading