github.com/Finschia/finschia-sdk@v0.48.1/server/rosetta/converter_test.go (about)

     1  package rosetta_test
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"testing"
     7  
     8  	"github.com/Finschia/finschia-sdk/testutil/testdata"
     9  	"github.com/Finschia/finschia-sdk/types/tx/signing"
    10  	authtx "github.com/Finschia/finschia-sdk/x/auth/tx"
    11  
    12  	rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
    13  	"github.com/stretchr/testify/suite"
    14  
    15  	abci "github.com/tendermint/tendermint/abci/types"
    16  
    17  	"github.com/Finschia/finschia-sdk/client"
    18  	"github.com/Finschia/finschia-sdk/codec"
    19  	codectypes "github.com/Finschia/finschia-sdk/codec/types"
    20  	"github.com/Finschia/finschia-sdk/server/rosetta"
    21  	crgerrs "github.com/Finschia/finschia-sdk/server/rosetta/lib/errors"
    22  	sdk "github.com/Finschia/finschia-sdk/types"
    23  	authsigning "github.com/Finschia/finschia-sdk/x/auth/signing"
    24  	bank "github.com/Finschia/finschia-sdk/x/bank/types"
    25  )
    26  
    27  type ConverterTestSuite struct {
    28  	suite.Suite
    29  
    30  	c               rosetta.Converter
    31  	unsignedTxBytes []byte
    32  	unsignedTx      authsigning.Tx
    33  
    34  	ir     codectypes.InterfaceRegistry
    35  	cdc    *codec.ProtoCodec
    36  	txConf client.TxConfig
    37  }
    38  
    39  // generateMsgSend generate sample unsignedTxHex and pubKeyHex
    40  func generateMsgSend() (unsignedTxHex []byte, pubKeyHex []byte) {
    41  	cdc, _ := rosetta.MakeCodec()
    42  	txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes)
    43  
    44  	_, fromPk, fromAddr := testdata.KeyTestPubAddr()
    45  	_, _, toAddr := testdata.KeyTestPubAddr()
    46  
    47  	sendMsg := bank.MsgSend{
    48  		FromAddress: fromAddr.String(),
    49  		ToAddress:   toAddr.String(),
    50  		Amount:      sdk.NewCoins(sdk.NewInt64Coin("stake", 16)),
    51  	}
    52  
    53  	txBuilder := txConfig.NewTxBuilder()
    54  	err := txBuilder.SetMsgs(&sendMsg)
    55  	if err != nil {
    56  		return nil, nil
    57  	}
    58  	txBuilder.SetGasLimit(200000)
    59  	txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewInt64Coin("stake", 1)))
    60  
    61  	sigData := signing.SingleSignatureData{
    62  		SignMode:  signing.SignMode_SIGN_MODE_DIRECT,
    63  		Signature: nil,
    64  	}
    65  	sig := signing.SignatureV2{
    66  		PubKey:   fromPk,
    67  		Data:     &sigData,
    68  		Sequence: 1,
    69  	}
    70  	err = txBuilder.SetSignatures(sig)
    71  	if err != nil {
    72  		return nil, nil
    73  	}
    74  
    75  	stdTx := txBuilder.GetTx()
    76  	unsignedTxHex, err = txConfig.TxEncoder()(stdTx)
    77  	if err != nil {
    78  		return nil, nil
    79  	}
    80  
    81  	return unsignedTxHex, fromPk.Bytes()
    82  }
    83  
    84  func (s *ConverterTestSuite) SetupTest() {
    85  	// create an unsigned tx
    86  	const unsignedTxHex = "0a8a010a87010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412670a2b6c696e6b3136773064766c61716e34727779706739757a36713878643778343434687568756d636370756e122b6c696e6b316c33757538657364636c6a3876706a72757737357535666a34773479746475396e6c6b6538721a0b0a057374616b651202313612640a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a210377365794209eab396f74316bb32ecb507c0e3788c14edf164f96b25cc4ef624112040a020801180112100a0a0a057374616b6512013110c09a0c1a00"
    87  	unsignedTxBytes, err := hex.DecodeString(unsignedTxHex)
    88  	s.Require().NoError(err)
    89  	s.unsignedTxBytes = unsignedTxBytes
    90  	// instantiate converter
    91  	cdc, ir := rosetta.MakeCodec()
    92  	txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes)
    93  	s.c = rosetta.NewConverter(cdc, ir, txConfig)
    94  	// add utils
    95  	s.ir = ir
    96  	s.cdc = cdc
    97  	s.txConf = txConfig
    98  	// add authsigning tx
    99  	sdkTx, err := txConfig.TxDecoder()(unsignedTxBytes)
   100  	s.Require().NoError(err)
   101  	builder, err := txConfig.WrapTxBuilder(sdkTx)
   102  	s.Require().NoError(err)
   103  
   104  	s.unsignedTx = builder.GetTx()
   105  }
   106  
   107  func (s *ConverterTestSuite) TestFromRosettaOpsToTxSuccess() {
   108  	addr1 := sdk.AccAddress("address1").String()
   109  	addr2 := sdk.AccAddress("address2").String()
   110  
   111  	msg1 := &bank.MsgSend{
   112  		FromAddress: addr1,
   113  		ToAddress:   addr2,
   114  		Amount:      sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
   115  	}
   116  
   117  	msg2 := &bank.MsgSend{
   118  		FromAddress: addr2,
   119  		ToAddress:   addr1,
   120  		Amount:      sdk.NewCoins(sdk.NewInt64Coin("utxo", 10)),
   121  	}
   122  
   123  	ops, err := s.c.ToRosetta().Ops("", msg1)
   124  	s.Require().NoError(err)
   125  
   126  	ops2, err := s.c.ToRosetta().Ops("", msg2)
   127  	s.Require().NoError(err)
   128  
   129  	ops = append(ops, ops2...)
   130  
   131  	tx, err := s.c.ToSDK().UnsignedTx(ops)
   132  	s.Require().NoError(err)
   133  
   134  	getMsgs := tx.GetMsgs()
   135  
   136  	s.Require().Equal(2, len(getMsgs))
   137  
   138  	s.Require().Equal(getMsgs[0], msg1)
   139  	s.Require().Equal(getMsgs[1], msg2)
   140  }
   141  
   142  func (s *ConverterTestSuite) TestFromRosettaOpsToTxErrors() {
   143  	s.Run("unrecognized op", func() {
   144  		op := &rosettatypes.Operation{
   145  			Type: "non-existent",
   146  		}
   147  
   148  		_, err := s.c.ToSDK().UnsignedTx([]*rosettatypes.Operation{op})
   149  
   150  		s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
   151  	})
   152  
   153  	s.Run("codec type but not sdk.Msg", func() {
   154  		op := &rosettatypes.Operation{
   155  			Type: "cosmos.crypto.ed25519.PubKey",
   156  		}
   157  
   158  		_, err := s.c.ToSDK().UnsignedTx([]*rosettatypes.Operation{op})
   159  
   160  		s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
   161  	})
   162  }
   163  
   164  func (s *ConverterTestSuite) TestMsgToMetaMetaToMsg() {
   165  	msg := &bank.MsgSend{
   166  		FromAddress: "addr1",
   167  		ToAddress:   "addr2",
   168  		Amount:      sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
   169  	}
   170  	msg.Route()
   171  
   172  	meta, err := s.c.ToRosetta().Meta(msg)
   173  	s.Require().NoError(err)
   174  
   175  	copyMsg := new(bank.MsgSend)
   176  	err = s.c.ToSDK().Msg(meta, copyMsg)
   177  	s.Require().NoError(err)
   178  	s.Require().Equal(msg, copyMsg)
   179  }
   180  
   181  func (s *ConverterTestSuite) TestSignedTx() {
   182  	s.Run("success", func() {
   183  		const payloadsJSON = `[{"hex_bytes":"82ccce81a3e4a7272249f0e25c3037a316ee2acce76eb0c25db00ef6634a4d57303b2420edfdb4c9a635ad8851fe5c7a9379b7bc2baadc7d74f7e76ac97459b5","public_key":{"curve_type":"secp256k1","hex_bytes":"0377365794209eab396f74316bb32ecb507c0e3788c14edf164f96b25cc4ef6241"},"signature_type":"ecdsa","signing_payload":{"account_identifier":{"address":"link16w0dvlaqn4rwypg9uz6q8xd7x444huhumccpun"},"address":"link16w0dvlaqn4rwypg9uz6q8xd7x444huhumccpun","hex_bytes":"ea43c4019ee3c888a7f99acb57513f708bb8915bc84e914cf4ecbd08ab2d9e51","signature_type":"ecdsa"}}]`
   184  		const expectedSignedTxHex = "0a8a010a87010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412670a2b6c696e6b3136773064766c61716e34727779706739757a36713878643778343434687568756d636370756e122b6c696e6b316c33757538657364636c6a3876706a72757737357535666a34773479746475396e6c6b6538721a0b0a057374616b651202313612640a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a210377365794209eab396f74316bb32ecb507c0e3788c14edf164f96b25cc4ef624112040a02087f180112100a0a0a057374616b6512013110c09a0c1a4082ccce81a3e4a7272249f0e25c3037a316ee2acce76eb0c25db00ef6634a4d57303b2420edfdb4c9a635ad8851fe5c7a9379b7bc2baadc7d74f7e76ac97459b5"
   185  
   186  		var payloads []*rosettatypes.Signature
   187  		s.Require().NoError(json.Unmarshal([]byte(payloadsJSON), &payloads))
   188  
   189  		signedTx, err := s.c.ToSDK().SignedTx(s.unsignedTxBytes, payloads)
   190  		s.Require().NoError(err)
   191  
   192  		signedTxHex := hex.EncodeToString(signedTx)
   193  
   194  		s.Require().Equal(signedTxHex, expectedSignedTxHex)
   195  	})
   196  
   197  	s.Run("signers data and signing payloads mismatch", func() {
   198  		_, err := s.c.ToSDK().SignedTx(s.unsignedTxBytes, nil)
   199  		s.Require().ErrorIs(err, crgerrs.ErrInvalidTransaction)
   200  	})
   201  }
   202  
   203  func (s *ConverterTestSuite) TestOpsAndSigners() {
   204  	s.Run("success", func() {
   205  		addr1 := sdk.AccAddress("address1").String()
   206  		addr2 := sdk.AccAddress("address2").String()
   207  
   208  		msg := &bank.MsgSend{
   209  			FromAddress: addr1,
   210  			ToAddress:   addr2,
   211  			Amount:      sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
   212  		}
   213  
   214  		builder := s.txConf.NewTxBuilder()
   215  		s.Require().NoError(builder.SetMsgs(msg))
   216  
   217  		sdkTx := builder.GetTx()
   218  		txBytes, err := s.txConf.TxEncoder()(sdkTx)
   219  		s.Require().NoError(err)
   220  
   221  		ops, signers, err := s.c.ToRosetta().OpsAndSigners(txBytes)
   222  		s.Require().NoError(err)
   223  
   224  		s.Require().Equal(len(ops), len(sdkTx.GetMsgs())*len(sdkTx.GetSigners()), "operation number mismatch")
   225  
   226  		s.Require().Equal(len(signers), len(sdkTx.GetSigners()), "signers number mismatch")
   227  	})
   228  }
   229  
   230  func (s *ConverterTestSuite) TestBeginEndBlockAndHashToTxType() {
   231  	const deliverTxHex = "5229A67AA008B5C5F1A0AEA77D4DEBE146297A30AAEF01777AF10FAD62DD36AB"
   232  
   233  	deliverTxBytes, err := hex.DecodeString(deliverTxHex)
   234  	s.Require().NoError(err)
   235  
   236  	endBlockTxHex := s.c.ToRosetta().EndBlockTxHash(deliverTxBytes)
   237  	beginBlockTxHex := s.c.ToRosetta().BeginBlockTxHash(deliverTxBytes)
   238  
   239  	txType, hash := s.c.ToSDK().HashToTxType(deliverTxBytes)
   240  
   241  	s.Require().Equal(rosetta.DeliverTxTx, txType)
   242  	s.Require().Equal(deliverTxBytes, hash, "deliver tx hash should not change")
   243  
   244  	endBlockTxBytes, err := hex.DecodeString(endBlockTxHex)
   245  	s.Require().NoError(err)
   246  
   247  	txType, hash = s.c.ToSDK().HashToTxType(endBlockTxBytes)
   248  
   249  	s.Require().Equal(rosetta.EndBlockTx, txType)
   250  	s.Require().Equal(deliverTxBytes, hash, "end block tx hash should be equal to a block hash")
   251  
   252  	beginBlockTxBytes, err := hex.DecodeString(beginBlockTxHex)
   253  	s.Require().NoError(err)
   254  
   255  	txType, hash = s.c.ToSDK().HashToTxType(beginBlockTxBytes)
   256  
   257  	s.Require().Equal(rosetta.BeginBlockTx, txType)
   258  	s.Require().Equal(deliverTxBytes, hash, "begin block tx hash should be equal to a block hash")
   259  
   260  	txType, hash = s.c.ToSDK().HashToTxType([]byte("invalid"))
   261  
   262  	s.Require().Equal(rosetta.UnrecognizedTx, txType)
   263  	s.Require().Nil(hash)
   264  
   265  	txType, hash = s.c.ToSDK().HashToTxType(append([]byte{0x3}, deliverTxBytes...))
   266  	s.Require().Equal(rosetta.UnrecognizedTx, txType)
   267  	s.Require().Nil(hash)
   268  }
   269  
   270  func (s *ConverterTestSuite) TestSigningComponents() {
   271  	s.Run("invalid metadata coins", func() {
   272  		_, _, err := s.c.ToRosetta().SigningComponents(nil, &rosetta.ConstructionMetadata{GasPrice: "invalid"}, nil)
   273  		s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
   274  	})
   275  
   276  	s.Run("length signers data does not match signers", func() {
   277  		_, _, err := s.c.ToRosetta().SigningComponents(s.unsignedTx, &rosetta.ConstructionMetadata{GasPrice: "10stake"}, nil)
   278  		s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
   279  	})
   280  
   281  	s.Run("length pub keys does not match signers", func() {
   282  		_, _, err := s.c.ToRosetta().SigningComponents(
   283  			s.unsignedTx,
   284  			&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
   285  				{
   286  					AccountNumber: 0,
   287  					Sequence:      0,
   288  				},
   289  			}},
   290  			nil)
   291  		s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
   292  	})
   293  
   294  	s.Run("ros pub key is valid but not the one we expect", func() {
   295  		validButUnexpected, err := hex.DecodeString("030da9096a40eb1d6c25f1e26e9cbf8941fc84b8f4dc509c8df5e62a29ab8f2415")
   296  		s.Require().NoError(err)
   297  
   298  		_, _, err = s.c.ToRosetta().SigningComponents(
   299  			s.unsignedTx,
   300  			&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
   301  				{
   302  					AccountNumber: 0,
   303  					Sequence:      0,
   304  				},
   305  			}},
   306  			[]*rosettatypes.PublicKey{
   307  				{
   308  					Bytes:     validButUnexpected,
   309  					CurveType: rosettatypes.Secp256k1,
   310  				},
   311  			})
   312  		s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
   313  	})
   314  
   315  	s.Run("success", func() {
   316  		expectedPubKey, err := hex.DecodeString("0377365794209eab396f74316bb32ecb507c0e3788c14edf164f96b25cc4ef6241")
   317  		s.Require().NoError(err)
   318  
   319  		_, _, err = s.c.ToRosetta().SigningComponents(
   320  			s.unsignedTx,
   321  			&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
   322  				{
   323  					AccountNumber: 0,
   324  					Sequence:      0,
   325  				},
   326  			}},
   327  			[]*rosettatypes.PublicKey{
   328  				{
   329  					Bytes:     expectedPubKey,
   330  					CurveType: rosettatypes.Secp256k1,
   331  				},
   332  			})
   333  		s.Require().NoError(err)
   334  	})
   335  }
   336  
   337  func (s *ConverterTestSuite) TestBalanceOps() {
   338  	s.Run("not a balance op", func() {
   339  		notBalanceOp := abci.Event{
   340  			Type: "not-a-balance-op",
   341  		}
   342  
   343  		ops := s.c.ToRosetta().BalanceOps("", []abci.Event{notBalanceOp})
   344  		s.Len(ops, 0, "expected no balance ops")
   345  	})
   346  
   347  	s.Run("multiple balance ops from 2 multicoins event", func() {
   348  		subBalanceOp := bank.NewCoinSpentEvent(
   349  			sdk.AccAddress("test"),
   350  			sdk.NewCoins(sdk.NewInt64Coin("test", 10), sdk.NewInt64Coin("utxo", 10)),
   351  		)
   352  
   353  		addBalanceOp := bank.NewCoinReceivedEvent(
   354  			sdk.AccAddress("test"),
   355  			sdk.NewCoins(sdk.NewInt64Coin("test", 10), sdk.NewInt64Coin("utxo", 10)),
   356  		)
   357  
   358  		ops := s.c.ToRosetta().BalanceOps("", []abci.Event{(abci.Event)(subBalanceOp), (abci.Event)(addBalanceOp)})
   359  		s.Len(ops, 4)
   360  	})
   361  
   362  	s.Run("spec broken", func() {
   363  		s.Require().Panics(func() {
   364  			specBrokenSub := abci.Event{
   365  				Type: bank.EventTypeCoinSpent,
   366  			}
   367  			_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
   368  		})
   369  
   370  		s.Require().Panics(func() {
   371  			specBrokenSub := abci.Event{
   372  				Type: bank.EventTypeCoinBurn,
   373  			}
   374  			_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
   375  		})
   376  
   377  		s.Require().Panics(func() {
   378  			specBrokenSub := abci.Event{
   379  				Type: bank.EventTypeCoinReceived,
   380  			}
   381  			_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
   382  		})
   383  	})
   384  }
   385  
   386  func TestConverterTestSuite(t *testing.T) {
   387  	suite.Run(t, new(ConverterTestSuite))
   388  }