349 lines
11 KiB
Go
349 lines
11 KiB
Go
package rosetta_test
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
|
|
|
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
|
|
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
|
crgerrs "github.com/cosmos/cosmos-sdk/server/rosetta/lib/errors"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
|
bank "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
)
|
|
|
|
type ConverterTestSuite struct {
|
|
suite.Suite
|
|
|
|
c rosetta.Converter
|
|
unsignedTxBytes []byte
|
|
unsignedTx authsigning.Tx
|
|
|
|
ir codectypes.InterfaceRegistry
|
|
cdc *codec.ProtoCodec
|
|
txConf client.TxConfig
|
|
}
|
|
|
|
func (s *ConverterTestSuite) SetupTest() {
|
|
// create an unsigned tx
|
|
const unsignedTxHex = "0a8e010a8b010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e64126b0a2d636f736d6f733134376b6c68377468356a6b6a793361616a736a3272717668747668396d666465333777713567122d636f736d6f73316d6e7670386c786b616679346c787777617175356561653764787630647a36687767797436331a0b0a057374616b651202313612600a4c0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad12020a0012100a0a0a057374616b651201311090a10f1a00"
|
|
unsignedTxBytes, err := hex.DecodeString(unsignedTxHex)
|
|
s.Require().NoError(err)
|
|
s.unsignedTxBytes = unsignedTxBytes
|
|
// instantiate converter
|
|
cdc, ir := rosetta.MakeCodec()
|
|
txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes)
|
|
s.c = rosetta.NewConverter(cdc, ir, txConfig)
|
|
// add utils
|
|
s.ir = ir
|
|
s.cdc = cdc
|
|
s.txConf = txConfig
|
|
// add authsigning tx
|
|
sdkTx, err := txConfig.TxDecoder()(unsignedTxBytes)
|
|
s.Require().NoError(err)
|
|
builder, err := txConfig.WrapTxBuilder(sdkTx)
|
|
s.Require().NoError(err)
|
|
|
|
s.unsignedTx = builder.GetTx()
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestFromRosettaOpsToTxSuccess() {
|
|
addr1 := sdk.AccAddress("address1").String()
|
|
addr2 := sdk.AccAddress("address2").String()
|
|
|
|
msg1 := &bank.MsgSend{
|
|
FromAddress: addr1,
|
|
ToAddress: addr2,
|
|
Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
|
|
}
|
|
|
|
msg2 := &bank.MsgSend{
|
|
FromAddress: addr2,
|
|
ToAddress: addr1,
|
|
Amount: sdk.NewCoins(sdk.NewInt64Coin("utxo", 10)),
|
|
}
|
|
|
|
ops, err := s.c.ToRosetta().Ops("", msg1)
|
|
s.Require().NoError(err)
|
|
|
|
ops2, err := s.c.ToRosetta().Ops("", msg2)
|
|
s.Require().NoError(err)
|
|
|
|
ops = append(ops, ops2...)
|
|
|
|
tx, err := s.c.ToSDK().UnsignedTx(ops)
|
|
s.Require().NoError(err)
|
|
|
|
getMsgs := tx.GetMsgs()
|
|
|
|
s.Require().Equal(2, len(getMsgs))
|
|
|
|
s.Require().Equal(getMsgs[0], msg1)
|
|
s.Require().Equal(getMsgs[1], msg2)
|
|
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestFromRosettaOpsToTxErrors() {
|
|
s.Run("unrecognized op", func() {
|
|
op := &rosettatypes.Operation{
|
|
Type: "non-existent",
|
|
}
|
|
|
|
_, err := s.c.ToSDK().UnsignedTx([]*rosettatypes.Operation{op})
|
|
|
|
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
|
|
})
|
|
|
|
s.Run("codec type but not sdk.Msg", func() {
|
|
op := &rosettatypes.Operation{
|
|
Type: "cosmos.crypto.ed25519.PubKey",
|
|
}
|
|
|
|
_, err := s.c.ToSDK().UnsignedTx([]*rosettatypes.Operation{op})
|
|
|
|
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestMsgToMetaMetaToMsg() {
|
|
msg := &bank.MsgSend{
|
|
FromAddress: "addr1",
|
|
ToAddress: "addr2",
|
|
Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
|
|
}
|
|
msg.Route()
|
|
|
|
meta, err := s.c.ToRosetta().Meta(msg)
|
|
s.Require().NoError(err)
|
|
|
|
copyMsg := new(bank.MsgSend)
|
|
err = s.c.ToSDK().Msg(meta, copyMsg)
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(msg, copyMsg)
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestSignedTx() {
|
|
|
|
s.Run("success", func() {
|
|
const payloadsJSON = `[{"hex_bytes":"82ccce81a3e4a7272249f0e25c3037a316ee2acce76eb0c25db00ef6634a4d57303b2420edfdb4c9a635ad8851fe5c7a9379b7bc2baadc7d74f7e76ac97459b5","signing_payload":{"address":"cosmos147klh7th5jkjy3aajsj2rqvhtvh9mfde37wq5g","hex_bytes":"ed574d84b095250280de38bf8c254e4a1f8755e5bd300b1f6ca2671688136ecc","account_identifier":{"address":"cosmos147klh7th5jkjy3aajsj2rqvhtvh9mfde37wq5g"},"signature_type":"ecdsa"},"public_key":{"hex_bytes":"034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad","curve_type":"secp256k1"},"signature_type":"ecdsa"}]`
|
|
const expectedSignedTxHex = "0a8e010a8b010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e64126b0a2d636f736d6f733134376b6c68377468356a6b6a793361616a736a3272717668747668396d666465333777713567122d636f736d6f73316d6e7670386c786b616679346c787777617175356561653764787630647a36687767797436331a0b0a057374616b651202313612620a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad12040a02087f12100a0a0a057374616b651201311090a10f1a4082ccce81a3e4a7272249f0e25c3037a316ee2acce76eb0c25db00ef6634a4d57303b2420edfdb4c9a635ad8851fe5c7a9379b7bc2baadc7d74f7e76ac97459b5"
|
|
|
|
var payloads []*rosettatypes.Signature
|
|
s.Require().NoError(json.Unmarshal([]byte(payloadsJSON), &payloads))
|
|
|
|
signedTx, err := s.c.ToSDK().SignedTx(s.unsignedTxBytes, payloads)
|
|
s.Require().NoError(err)
|
|
|
|
signedTxHex := hex.EncodeToString(signedTx)
|
|
|
|
s.Require().Equal(signedTxHex, expectedSignedTxHex)
|
|
})
|
|
|
|
s.Run("signers data and signing payloads mismatch", func() {
|
|
_, err := s.c.ToSDK().SignedTx(s.unsignedTxBytes, nil)
|
|
s.Require().ErrorIs(err, crgerrs.ErrInvalidTransaction)
|
|
})
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestOpsAndSigners() {
|
|
s.Run("success", func() {
|
|
addr1 := sdk.AccAddress("address1").String()
|
|
addr2 := sdk.AccAddress("address2").String()
|
|
|
|
msg := &bank.MsgSend{
|
|
FromAddress: addr1,
|
|
ToAddress: addr2,
|
|
Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
|
|
}
|
|
|
|
builder := s.txConf.NewTxBuilder()
|
|
s.Require().NoError(builder.SetMsgs(msg))
|
|
|
|
sdkTx := builder.GetTx()
|
|
txBytes, err := s.txConf.TxEncoder()(sdkTx)
|
|
s.Require().NoError(err)
|
|
|
|
ops, signers, err := s.c.ToRosetta().OpsAndSigners(txBytes)
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal(len(ops), len(sdkTx.GetMsgs())*len(sdkTx.GetSigners()), "operation number mismatch")
|
|
|
|
s.Require().Equal(len(signers), len(sdkTx.GetSigners()), "signers number mismatch")
|
|
})
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestBeginEndBlockAndHashToTxType() {
|
|
const deliverTxHex = "5229A67AA008B5C5F1A0AEA77D4DEBE146297A30AAEF01777AF10FAD62DD36AB"
|
|
|
|
deliverTxBytes, err := hex.DecodeString(deliverTxHex)
|
|
s.Require().NoError(err)
|
|
|
|
endBlockTxHex := s.c.ToRosetta().EndBlockTxHash(deliverTxBytes)
|
|
beginBlockTxHex := s.c.ToRosetta().BeginBlockTxHash(deliverTxBytes)
|
|
|
|
txType, hash := s.c.ToSDK().HashToTxType(deliverTxBytes)
|
|
|
|
s.Require().Equal(rosetta.DeliverTxTx, txType)
|
|
s.Require().Equal(deliverTxBytes, hash, "deliver tx hash should not change")
|
|
|
|
endBlockTxBytes, err := hex.DecodeString(endBlockTxHex)
|
|
s.Require().NoError(err)
|
|
|
|
txType, hash = s.c.ToSDK().HashToTxType(endBlockTxBytes)
|
|
|
|
s.Require().Equal(rosetta.EndBlockTx, txType)
|
|
s.Require().Equal(deliverTxBytes, hash, "end block tx hash should be equal to a block hash")
|
|
|
|
beginBlockTxBytes, err := hex.DecodeString(beginBlockTxHex)
|
|
s.Require().NoError(err)
|
|
|
|
txType, hash = s.c.ToSDK().HashToTxType(beginBlockTxBytes)
|
|
|
|
s.Require().Equal(rosetta.BeginBlockTx, txType)
|
|
s.Require().Equal(deliverTxBytes, hash, "begin block tx hash should be equal to a block hash")
|
|
|
|
txType, hash = s.c.ToSDK().HashToTxType([]byte("invalid"))
|
|
|
|
s.Require().Equal(rosetta.UnrecognizedTx, txType)
|
|
s.Require().Nil(hash)
|
|
|
|
txType, hash = s.c.ToSDK().HashToTxType(append([]byte{0x3}, deliverTxBytes...))
|
|
s.Require().Equal(rosetta.UnrecognizedTx, txType)
|
|
s.Require().Nil(hash)
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestSigningComponents() {
|
|
s.Run("invalid metadata coins", func() {
|
|
_, _, err := s.c.ToRosetta().SigningComponents(nil, &rosetta.ConstructionMetadata{GasPrice: "invalid"}, nil)
|
|
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
|
|
})
|
|
|
|
s.Run("length signers data does not match signers", func() {
|
|
_, _, err := s.c.ToRosetta().SigningComponents(s.unsignedTx, &rosetta.ConstructionMetadata{GasPrice: "10stake"}, nil)
|
|
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
|
|
})
|
|
|
|
s.Run("length pub keys does not match signers", func() {
|
|
_, _, err := s.c.ToRosetta().SigningComponents(
|
|
s.unsignedTx,
|
|
&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
|
|
{
|
|
AccountNumber: 0,
|
|
Sequence: 0,
|
|
},
|
|
}},
|
|
nil)
|
|
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
|
|
})
|
|
|
|
s.Run("ros pub key is valid but not the one we expect", func() {
|
|
validButUnexpected, err := hex.DecodeString("030da9096a40eb1d6c25f1e26e9cbf8941fc84b8f4dc509c8df5e62a29ab8f2415")
|
|
s.Require().NoError(err)
|
|
|
|
_, _, err = s.c.ToRosetta().SigningComponents(
|
|
s.unsignedTx,
|
|
&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
|
|
{
|
|
AccountNumber: 0,
|
|
Sequence: 0,
|
|
},
|
|
}},
|
|
[]*rosettatypes.PublicKey{
|
|
{
|
|
Bytes: validButUnexpected,
|
|
CurveType: rosettatypes.Secp256k1,
|
|
},
|
|
})
|
|
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
|
|
})
|
|
|
|
s.Run("success", func() {
|
|
expectedPubKey, err := hex.DecodeString("034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad")
|
|
s.Require().NoError(err)
|
|
|
|
_, _, err = s.c.ToRosetta().SigningComponents(
|
|
s.unsignedTx,
|
|
&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
|
|
{
|
|
AccountNumber: 0,
|
|
Sequence: 0,
|
|
},
|
|
}},
|
|
[]*rosettatypes.PublicKey{
|
|
{
|
|
Bytes: expectedPubKey,
|
|
CurveType: rosettatypes.Secp256k1,
|
|
},
|
|
})
|
|
s.Require().NoError(err)
|
|
})
|
|
|
|
}
|
|
|
|
func (s *ConverterTestSuite) TestBalanceOps() {
|
|
s.Run("not a balance op", func() {
|
|
notBalanceOp := abci.Event{
|
|
Type: "not-a-balance-op",
|
|
}
|
|
|
|
ops := s.c.ToRosetta().BalanceOps("", []abci.Event{notBalanceOp})
|
|
s.Len(ops, 0, "expected no balance ops")
|
|
})
|
|
|
|
s.Run("multiple balance ops from 2 multicoins event", func() {
|
|
subBalanceOp := bank.NewCoinSpentEvent(
|
|
sdk.AccAddress("test"),
|
|
sdk.NewCoins(sdk.NewInt64Coin("test", 10), sdk.NewInt64Coin("utxo", 10)),
|
|
)
|
|
|
|
addBalanceOp := bank.NewCoinReceivedEvent(
|
|
sdk.AccAddress("test"),
|
|
sdk.NewCoins(sdk.NewInt64Coin("test", 10), sdk.NewInt64Coin("utxo", 10)),
|
|
)
|
|
|
|
ops := s.c.ToRosetta().BalanceOps("", []abci.Event{(abci.Event)(subBalanceOp), (abci.Event)(addBalanceOp)})
|
|
s.Len(ops, 4)
|
|
})
|
|
|
|
s.Run("spec broken", func() {
|
|
s.Require().Panics(func() {
|
|
specBrokenSub := abci.Event{
|
|
Type: bank.EventTypeCoinSpent,
|
|
}
|
|
_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
|
|
})
|
|
|
|
s.Require().Panics(func() {
|
|
specBrokenSub := abci.Event{
|
|
Type: bank.EventTypeCoinBurn,
|
|
}
|
|
_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
|
|
})
|
|
|
|
s.Require().Panics(func() {
|
|
specBrokenSub := abci.Event{
|
|
Type: bank.EventTypeCoinReceived,
|
|
}
|
|
_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestConverterTestSuite(t *testing.T) {
|
|
suite.Run(t, new(ConverterTestSuite))
|
|
}
|