Skip to content

Commit

Permalink
Merge pull request #135 from centrifuge/update-event-parsing
Browse files Browse the repository at this point in the history
listener: Update event parsing
  • Loading branch information
cdamian authored Jul 17, 2023
2 parents fec96ed + 85ed235 commit b8c608c
Show file tree
Hide file tree
Showing 6 changed files with 319 additions and 99 deletions.
11 changes: 10 additions & 1 deletion chains/substrate/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ As the writer receives messages from the router, it constructs proposals. If a p
package substrate

import (
"fmt"
"github.com/ChainSafe/log15"
"github.com/centrifuge/chainbridge-utils/blockstore"
"github.com/centrifuge/chainbridge-utils/core"
"github.com/centrifuge/chainbridge-utils/crypto/sr25519"
"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/retriever"
"github.com/centrifuge/go-substrate-rpc-client/v4/registry/state"
)

var _ core.Chain = &Chain{}
Expand Down Expand Up @@ -102,8 +105,14 @@ 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)

if err != nil {
return nil, fmt.Errorf("event retriever creation: %w", err)
}

// Setup listener & writer
l := NewListener(conn, cfg.Name, cfg.Id, startBlock, logger, bs, stop, sysErr, m)
l := NewListener(conn, cfg.Name, cfg.Id, startBlock, logger, bs, stop, sysErr, m, eventRetriever)
w := NewWriter(conn, logger, sysErr, m, ue)
return &Chain{
cfg: cfg,
Expand Down
2 changes: 1 addition & 1 deletion chains/substrate/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func (c *Connection) getMetadata() (meta types.Metadata) {
return meta
}

func (c *Connection) updateMetatdata() error {
func (c *Connection) updateMetadata() error {
c.metaLock.Lock()
meta, err := c.api.RPC.State.GetMetadataLatest()
if err != nil {
Expand Down
275 changes: 235 additions & 40 deletions chains/substrate/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
package substrate

import (
"errors"
"fmt"
"math/big"

"github.com/ChainSafe/log15"
events "github.com/centrifuge/chainbridge-substrate-events"
"github.com/centrifuge/chainbridge-utils/msg"
"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(interface{}, log15.Logger) (msg.Message, error)
type eventHandler func(map[string]any, log15.Logger) (msg.Message, error)

const FungibleTransfer eventName = "FungibleTransfer"
const NonFungibleTransfer eventName = "NonFungibleTransfer"
const GenericTransfer eventName = "GenericTransfer"
const FungibleTransfer eventName = "ChainBridge.FungibleTransfer"
const NonFungibleTransfer eventName = "ChainBridge.NonFungibleTransfer"
const GenericTransfer eventName = "ChainBridge.GenericTransfer"

var Subscriptions = []struct {
name eventName
Expand All @@ -28,57 +30,250 @@ var Subscriptions = []struct {
{GenericTransfer, genericTransferHandler},
}

func fungibleTransferHandler(evtI interface{}, log log15.Logger) (msg.Message, error) {
evt, ok := evtI.(events.EventFungibleTransfer)
if !ok {
return msg.Message{}, fmt.Errorf("failed to cast EventFungibleTransfer type")
func fungibleTransferHandler(eventFields map[string]any, log log15.Logger) (msg.Message, error) {
chainID, err := getFieldValueAsType[types.U8]("ChainId", eventFields)
if err != nil {
return msg.Message{}, err
}

resourceId := msg.ResourceId(evt.ResourceId)
log.Info("Got fungible transfer event!", "destination", evt.Destination, "resourceId", resourceId.Hex(), "amount", evt.Amount)
depositNonce, err := getFieldValueAsType[types.U64]("DepositNonce", eventFields)
if err != nil {
return msg.Message{}, err
}

resID, err := getFieldValueAsSliceOfType[types.U8]("ResourceId", eventFields)
if err != nil {
return msg.Message{}, err
}

resourceID, err := to32Bytes(resID)
if err != nil {
return msg.Message{}, err
}

amount, err := getU256(eventFields)
if err != nil {
return msg.Message{}, err
}

recipient, err := getFieldValueAsByteSlice("Vec<u8>", eventFields)
if err != nil {
return msg.Message{}, err
}

log.Info("Got fungible transfer event!", "destination", recipient, "resourceId", fmt.Sprintf("%x", resourceID), "amount", amount)

return msg.NewFungibleTransfer(
0, // Unset
msg.ChainId(evt.Destination),
msg.Nonce(evt.DepositNonce),
evt.Amount.Int,
resourceId,
evt.Recipient,
msg.ChainId(chainID),
msg.Nonce(depositNonce),
amount.Int,
resourceID,
recipient,
), nil
}

func nonFungibleTransferHandler(evtI interface{}, log log15.Logger) (msg.Message, error) {
evt, ok := evtI.(events.EventNonFungibleTransfer)
if !ok {
return msg.Message{}, fmt.Errorf("failed to cast EventNonFungibleTransfer type")
func nonFungibleTransferHandler(_ map[string]any, 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) {
chainID, err := getFieldValueAsType[types.U8]("ChainId", eventFields)
if err != nil {
return msg.Message{}, err
}

log.Info("Got non-fungible transfer event!", "destination", evt.Destination, "resourceId", evt.ResourceId)
depositNonce, err := getFieldValueAsType[types.U64]("DepositNonce", eventFields)
if err != nil {
return msg.Message{}, err
}

return msg.NewNonFungibleTransfer(
0, // Unset
msg.ChainId(evt.Destination),
msg.Nonce(evt.DepositNonce),
msg.ResourceId(evt.ResourceId),
big.NewInt(0).SetBytes(evt.TokenId[:]),
evt.Recipient,
evt.Metadata,
), nil
}
resID, err := getFieldValueAsSliceOfType[types.U8]("ResourceId", eventFields)
if err != nil {
return msg.Message{}, err
}

resourceID, err := to32Bytes(resID)
if err != nil {
return msg.Message{}, err
}

func genericTransferHandler(evtI interface{}, log log15.Logger) (msg.Message, error) {
evt, ok := evtI.(events.EventGenericTransfer)
if !ok {
return msg.Message{}, fmt.Errorf("failed to cast EventGenericTransfer type")
metadata, err := getFieldValueAsByteSlice("Vec<u8>", eventFields)
if err != nil {
return msg.Message{}, err
}

log.Info("Got generic transfer event!", "destination", evt.Destination, "resourceId", evt.ResourceId)
log.Info("Got generic transfer event!", "destination", chainID, "resourceId", fmt.Sprintf("%x", resourceID))

return msg.NewGenericTransfer(
0, // Unset
msg.ChainId(evt.Destination),
msg.Nonce(evt.DepositNonce),
msg.ResourceId(evt.ResourceId),
evt.Metadata,
msg.ChainId(chainID),
msg.Nonce(depositNonce),
resourceID,
metadata,
), nil
}

func to32Bytes(array []types.U8) ([32]byte, error) {
var res [32]byte

if len(array) != 32 {
return res, errors.New("array length mismatch")
}

for i, item := range array {
res[i] = byte(item)
}

return res, nil
}

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

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

return t, fmt.Errorf("field type mismatch, expected %T, got %T", t, 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)

if !ok {
return nil, errors.New("field value not an array")
}

res, err := convertSliceToType[T](value)

if err != nil {
return nil, err
}

return res, nil
}
}

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)

if !ok {
return nil, errors.New("field value not an array")
}

slice, err := convertSliceToType[types.U8](value)

if err != nil {
return nil, err
}

res, err := convertToByteSlice(slice)

if err != nil {
return nil, err
}

return res, nil
}
}

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

func convertSliceToType[T any](array []any) ([]T, error) {
res := make([]T, 0)

for _, item := range array {
if v, ok := item.(T); ok {
res = append(res, v)
continue
}

var t T

return nil, fmt.Errorf("couldn't cast '%T' to '%T'", item, t)
}

return res, nil
}

func convertToByteSlice(array []types.U8) ([]byte, error) {
res := make([]byte, 0)

for _, item := range array {
res = append(res, byte(item))
}

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

0 comments on commit b8c608c

Please sign in to comment.