Skip to content

Commit

Permalink
batteries to power, turbos to speed
Browse files Browse the repository at this point in the history
  • Loading branch information
adamewozniak committed Nov 14, 2023
1 parent 143e829 commit 2c523d9
Show file tree
Hide file tree
Showing 12 changed files with 266 additions and 188 deletions.
8 changes: 4 additions & 4 deletions app/gmpmiddleware/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (h GmpHandler) HandleGeneralMessage(
if err != nil {
return err
}
msg, err := NewGmpData(payload)
msg, err := types.NewGmpDecoder(payload)
if err != nil {
return err
}
Expand All @@ -58,7 +58,7 @@ func (h GmpHandler) HandleGeneralMessage(
Relayer: srcAddress,
DestinationChain: srcChain,
DestinationAddress: destAddress,
Denoms: msg.assets(),
Denoms: msg.GetDenoms(),
},
)
return err
Expand Down Expand Up @@ -88,7 +88,7 @@ func (h GmpHandler) HandleGeneralMessageWithToken(
if err != nil {
return err
}
msg, err := NewGmpData(payload)
msg, err := types.NewGmpDecoder(payload)
if err != nil {
return err
}
Expand All @@ -97,7 +97,7 @@ func (h GmpHandler) HandleGeneralMessageWithToken(
Relayer: srcAddress,
DestinationChain: srcChain,
DestinationAddress: destAddress,
Denoms: msg.assets(),
Denoms: msg.GetDenoms(),
Token: coin,
},
)
Expand Down
88 changes: 0 additions & 88 deletions app/gmpmiddleware/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ package gmpmiddleware

import (
"fmt"
"math/big"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/ibc-go/v7/modules/apps/transfer/types"
channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
gmptypes "github.com/ojo-network/ojo/x/gmp/types"
)

Expand Down Expand Up @@ -43,91 +40,6 @@ type Message struct {
Type int64 `json:"type"`
}

// GmpData is the payload sent from Axelar to IBC middleware.
// It needs to be decoded using the ABI.
type GmpData struct {
AssetNames [][32]byte // bytes32 in Solidity is represented as [32]byte in Go
ContractAddress common.Address // address in Solidity is represented as common.Address in Go
CommandSelector [4]byte // bytes4 in Solidity is represented as [4]byte in Go
CommandParams []byte // bytes in Solidity is represented as []byte in Go
Timestamp *big.Int // uint256 in Solidity is represented as *big.Int in Go
AbiEncodedData []byte // the ABI encoded data
}

var (
assetNamesType, _ = abi.NewType("bytes32[]", "bytes32[]", nil)
contractAddressType, _ = abi.NewType("address", "address", nil)
commandSelectorType, _ = abi.NewType("bytes4", "bytes4", nil)
commandParamsType, _ = abi.NewType("bytes", "bytes", nil)
timestampType, _ = abi.NewType("uint256", "uint256", nil)
)

var abiSpec = abi.Arguments{
{
Type: assetNamesType,
},
{
Type: contractAddressType,
},
{
Type: commandSelectorType,
},
{
Type: commandParamsType,
},
{
Type: timestampType,
},
}

// NewGmpData decodes a payload from GMP given a byte array
func NewGmpData(payload []byte) (GmpData, error) {
args, err := abiSpec.Unpack(payload)
if err != nil {
return GmpData{}, err
}

// check to make sure each argument is the correct type
if assetNames, ok := args[0].([][32]byte); !ok {
return GmpData{}, fmt.Errorf("invalid asset names type: %T", args[0])
} else if contractAddress, ok := args[1].(common.Address); !ok {
return GmpData{}, fmt.Errorf("invalid contract address type: %T", args[1])
} else if commandSelector, ok := args[2].([4]byte); !ok {
return GmpData{}, fmt.Errorf("invalid command selector type: %T", args[2])
} else if commandParams, ok := args[3].([]byte); !ok {
return GmpData{}, fmt.Errorf("invalid command params type: %T", args[3])
} else if timestamp, ok := args[4].(*big.Int); !ok {
return GmpData{}, fmt.Errorf("invalid timestamp type: %T", args[4])
} else {
return GmpData{
AssetNames: assetNames,
ContractAddress: contractAddress,
CommandSelector: commandSelector,
CommandParams: commandParams,
Timestamp: timestamp,
}, nil
}
}

// assets takes a GmpData and returns the asset names as a slice of strings
func (g GmpData) assets() []string {
var assetNames []string
for _, assetName := range g.AssetNames {
assetNames = append(assetNames, string(assetName[:]))
}
return assetNames
}

func (g GmpData) Encode() ([]byte, error) {
return abiSpec.Pack(
g.AssetNames,
g.ContractAddress,
g.CommandSelector,
g.CommandParams,
g.Timestamp,
)
}

func verifyParams(params gmptypes.Params, sender string, channel string) error {
if !strings.EqualFold(params.GmpAddress, sender) {
return fmt.Errorf("invalid sender address: %s", sender)
Expand Down
19 changes: 0 additions & 19 deletions app/gmpmiddleware/types_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package gmpmiddleware

import (
"math/big"
"testing"

channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ojo-network/ojo/x/gmp/types"
"github.com/stretchr/testify/require"
)
Expand All @@ -22,23 +20,6 @@ func TestVerifyParams(t *testing.T) {
require.Error(t, err)
}

// TestGmpData tests the GmpData struct by encoding and decoding it.
func TestGmpData(t *testing.T) {
gmpData := GmpData{
AssetNames: [][32]byte{{1}},
ContractAddress: common.HexToAddress("0x0000001"),
CommandSelector: [4]byte{1},
CommandParams: []byte{1},
Timestamp: big.NewInt(1),
}
payload, err := gmpData.Encode()
require.NoError(t, err)
newGmpData, err := NewGmpData(payload)
require.NoError(t, err)

require.Equal(t, gmpData, newGmpData)
}

func TestParseDenom(t *testing.T) {
packet := channeltypes.Packet{
SourcePort: "ibc",
Expand Down
6 changes: 3 additions & 3 deletions x/gmp/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,16 @@ func (k Keeper) RelayPrice(

// TODO: fill with actual disableResolve option
// Ref: https://github.com/ojo-network/ojo/issues/309
payload, err := types.EncodeABI("postPrices", rates, false)
/*payload, err := types.EncodeABI("postPrices", rates, false)
if err != nil {
return nil, err
}
}*/

// package GMP
message := types.GmpMessage{
DestinationChain: msg.DestinationChain,
DestinationAddress: msg.DestinationAddress,
Payload: payload,
Payload: []byte{},
Type: types.TypeGeneralMessage,
}
bz, err := message.Marshal()
Expand Down
77 changes: 77 additions & 0 deletions x/gmp/types/abi_decode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package types

import (
fmt "fmt"
"math/big"
"strings"

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

// GmpDecoder is the payload sent from Axelar to IBC middleware.
// It needs to be decoded using the ABI.
type GmpDecoder struct {
AssetNames [][32]byte
ContractAddress common.Address
CommandSelector [4]byte
CommandParams []byte
Timestamp *big.Int
AbiEncodedData []byte
}

// abiSpec is the ABI specification for the GMP data.
var decoderSpec = abi.Arguments{
{
Type: assetNamesType,
},
{
Type: contractAddressType,
},
{
Type: commandSelectorType,
},
{
Type: commandParamsType,
},
{
Type: timestampType,
},
}

// NewGmpDecoder decodes a payload from GMP given a byte array
func NewGmpDecoder(payload []byte) (GmpDecoder, error) {
args, err := decoderSpec.Unpack(payload)
if err != nil {
return GmpDecoder{}, err
}

// check to make sure each argument is the correct type
if assetNames, ok := args[0].([][32]byte); !ok {
return GmpDecoder{}, fmt.Errorf("invalid asset names type: %T", args[0])
} else if contractAddress, ok := args[1].(common.Address); !ok {
return GmpDecoder{}, fmt.Errorf("invalid contract address type: %T", args[1])
} else if commandSelector, ok := args[2].([4]byte); !ok {
return GmpDecoder{}, fmt.Errorf("invalid command selector type: %T", args[2])
} else if commandParams, ok := args[3].([]byte); !ok {
return GmpDecoder{}, fmt.Errorf("invalid command params type: %T", args[3])
} else if timestamp, ok := args[4].(*big.Int); !ok {
return GmpDecoder{}, fmt.Errorf("invalid timestamp type: %T", args[4])
} else {
return GmpDecoder{
AssetNames: assetNames,
ContractAddress: contractAddress,
CommandSelector: commandSelector,
CommandParams: commandParams,
Timestamp: timestamp,
}, nil
}
}

func (g GmpDecoder) GetDenoms() []string {
denoms := make([]string, len(g.AssetNames))
for i, name := range g.AssetNames {
denoms[i] = strings.TrimRight(string(name[:]), "\x00")
}
return denoms
}
53 changes: 53 additions & 0 deletions x/gmp/types/abi_decode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package types

import (
"math/big"
"testing"

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

// TestGmpData tests the GmpData struct by encoding and decoding it.
func TestGmpData(t *testing.T) {
g := GmpDecoder{
AssetNames: [][32]byte{{1}},
ContractAddress: common.HexToAddress("0x0000001"),
CommandSelector: [4]byte{1},
CommandParams: []byte{1},
Timestamp: big.NewInt(1),
}
payload, err := decoderSpec.Pack(
g.AssetNames,
g.ContractAddress,
g.CommandSelector,
g.CommandParams,
g.Timestamp,
)
require.NoError(t, err)
newGmpData, err := NewGmpDecoder(payload)
require.NoError(t, err)

require.Equal(t, g, newGmpData)
}

func TestGetDenoms(t *testing.T) {
assetNamesString := []string{
"BTC",
"ETH",
"USDT",
"BNB",
"ADA",
"FOOBARFOOBARFOOBARFOOBARFOOBARFO", // maxmimum allowed length
}
assetNamesAsBytes := make([][32]byte, len(assetNamesString))
for i, name := range assetNamesString {
copy(assetNamesAsBytes[i][:], name)
}

g := GmpDecoder{
AssetNames: assetNamesAsBytes,
}
denoms := g.GetDenoms()
require.Equal(t, assetNamesString, denoms)
}
56 changes: 56 additions & 0 deletions x/gmp/types/abi_encode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package types

import (
"math/big"

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

// GmpEncoder is the struct we use to encode the data we want to send to the GMP.
type GmpEncoder struct {
PriceData []PriceData
AssetNames [32]byte
ContractAddress common.Address
CommandSelector [4]byte
CommandParams []byte
}

// MedianData is the struct that represents the MedianData tuple in Solidity.
type MedianData struct {
BlockNums []*big.Int
Medians []*big.Int
Deviations []*big.Int
}

// PriceData is the struct that represents the PriceData tuple in Solidity.
type PriceData struct {
AssetName [32]byte
Price *big.Int
ResolveTime *big.Int
MedianData []MedianData
}

// encoderSpec is the ABI specification for the GMP data.
var encoderSpec = abi.Arguments{
{
Type: priceDataType,
},
{
Type: assetNameType,
},
{
Type: contractAddressType,
},
{
Type: commandSelectorType,
},
{
Type: commandParamsType,
},
}

// GMPEncode encodes the GMP data into a byte array.
func (g GmpEncoder) GMPEncode() ([]byte, error) {
return encoderSpec.Pack(g.PriceData, g.AssetNames, g.ContractAddress, g.CommandSelector, g.CommandParams)
}
Loading

0 comments on commit 2c523d9

Please sign in to comment.