feat(x/tx): legacy amino json sign mode handler (#15515)
This commit is contained in:
parent
723256f769
commit
5e90819765
@ -17,3 +17,6 @@ echo "Generating API module"
|
||||
|
||||
echo "Generate Pulsar Test Data"
|
||||
(cd testutil/testdata; buf generate --template buf.gen.pulsar.yaml)
|
||||
|
||||
echo "Generate x/tx"
|
||||
(cd x/tx; make codegen)
|
||||
@ -195,6 +195,7 @@ replace (
|
||||
cosmossdk.io/x/evidence => ../x/evidence
|
||||
cosmossdk.io/x/feegrant => ../x/feegrant
|
||||
cosmossdk.io/x/nft => ../x/nft
|
||||
cosmossdk.io/x/tx => ../x/tx
|
||||
cosmossdk.io/x/upgrade => ../x/upgrade
|
||||
)
|
||||
|
||||
|
||||
@ -204,8 +204,6 @@ cosmossdk.io/log v1.0.0 h1:NGKZ/A5rd4PduDfoscgABklX557PWjQINbosZy/m3Jk=
|
||||
cosmossdk.io/log v1.0.0/go.mod h1:CwX9BLiBruZb7lzLlRr3R231d/fVPUXk8gAdV4LQap0=
|
||||
cosmossdk.io/math v1.0.0 h1:ro9w7eKx23om2tZz/VM2Pf+z2WAbGX1yDQQOJ6iGeJw=
|
||||
cosmossdk.io/math v1.0.0/go.mod h1:Ygz4wBHrgc7g0N+8+MrnTfS9LLn9aaTGa9hKopuym5k=
|
||||
cosmossdk.io/x/tx v0.5.0 h1:01wPSoiYDHlfudV0fn867SBXI3uI/8tpatBgVVSnFzI=
|
||||
cosmossdk.io/x/tx v0.5.0/go.mod h1:kDcwrN6QbCj+9NXVHL8qipMzA9YlMr9NpZa08SHCdLA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
@ -38,6 +39,7 @@ import (
|
||||
paramsapi "cosmossdk.io/api/cosmos/params/v1beta1"
|
||||
slashingapi "cosmossdk.io/api/cosmos/slashing/v1beta1"
|
||||
stakingapi "cosmossdk.io/api/cosmos/staking/v1beta1"
|
||||
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
|
||||
upgradeapi "cosmossdk.io/api/cosmos/upgrade/v1beta1"
|
||||
vestingapi "cosmossdk.io/api/cosmos/vesting/v1beta1"
|
||||
"cosmossdk.io/x/evidence"
|
||||
@ -45,6 +47,7 @@ import (
|
||||
feegranttypes "cosmossdk.io/x/feegrant"
|
||||
feegrantmodule "cosmossdk.io/x/feegrant/module"
|
||||
"cosmossdk.io/x/tx/signing/aminojson"
|
||||
signing_testutil "cosmossdk.io/x/tx/signing/testutil"
|
||||
"cosmossdk.io/x/upgrade"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
@ -57,7 +60,10 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
"github.com/cosmos/cosmos-sdk/types/module/testutil"
|
||||
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
||||
@ -367,6 +373,50 @@ func TestAminoJSON_Equivalence(t *testing.T) {
|
||||
aminoJSON, err := aj.Marshal(msg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(legacyAminoJSON), string(aminoJSON))
|
||||
|
||||
// test amino json signer handler equivalence
|
||||
gogoMsg, ok := gogo.(types.Msg)
|
||||
if !ok {
|
||||
// not signable
|
||||
return
|
||||
}
|
||||
|
||||
handlerOptions := signing_testutil.HandlerArgumentOptions{
|
||||
ChainId: "test-chain",
|
||||
Memo: "sometestmemo",
|
||||
Msg: tt.pulsar,
|
||||
AccNum: 1,
|
||||
AccSeq: 2,
|
||||
SignerAddress: "signerAddress",
|
||||
Fee: &txv1beta1.Fee{
|
||||
Amount: []*v1beta1.Coin{{Denom: "uatom", Amount: "1000"}},
|
||||
},
|
||||
}
|
||||
|
||||
signerData, txData, err := signing_testutil.MakeHandlerArguments(handlerOptions)
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{})
|
||||
signBz, err := handler.GetSignBytes(context.Background(), signerData, txData)
|
||||
require.NoError(t, err)
|
||||
|
||||
legacyHandler := tx.NewSignModeLegacyAminoJSONHandler()
|
||||
txBuilder := encCfg.TxConfig.NewTxBuilder()
|
||||
require.NoError(t, txBuilder.SetMsgs([]types.Msg{gogoMsg}...))
|
||||
txBuilder.SetMemo(handlerOptions.Memo)
|
||||
txBuilder.SetFeeAmount(types.Coins{types.NewInt64Coin("uatom", 1000)})
|
||||
theTx := txBuilder.GetTx()
|
||||
|
||||
legacySigningData := signing.SignerData{
|
||||
ChainID: handlerOptions.ChainId,
|
||||
Address: handlerOptions.SignerAddress,
|
||||
AccountNumber: handlerOptions.AccNum,
|
||||
Sequence: handlerOptions.AccSeq,
|
||||
}
|
||||
legacySignBz, err := legacyHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
legacySigningData, theTx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(legacySignBz), string(signBz))
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -624,6 +674,50 @@ func TestAminoJSON_LegacyParity(t *testing.T) {
|
||||
return
|
||||
}
|
||||
require.Equal(t, string(gogoBytes), string(newGogoBytes))
|
||||
|
||||
// test amino json signer handler equivalence
|
||||
msg, ok := tc.gogo.(types.Msg)
|
||||
if !ok {
|
||||
// not signable
|
||||
return
|
||||
}
|
||||
|
||||
handlerOptions := signing_testutil.HandlerArgumentOptions{
|
||||
ChainId: "test-chain",
|
||||
Memo: "sometestmemo",
|
||||
Msg: tc.pulsar,
|
||||
AccNum: 1,
|
||||
AccSeq: 2,
|
||||
SignerAddress: "signerAddress",
|
||||
Fee: &txv1beta1.Fee{
|
||||
Amount: []*v1beta1.Coin{{Denom: "uatom", Amount: "1000"}},
|
||||
},
|
||||
}
|
||||
|
||||
signerData, txData, err := signing_testutil.MakeHandlerArguments(handlerOptions)
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{})
|
||||
signBz, err := handler.GetSignBytes(context.Background(), signerData, txData)
|
||||
require.NoError(t, err)
|
||||
|
||||
legacyHandler := tx.NewSignModeLegacyAminoJSONHandler()
|
||||
txBuilder := encCfg.TxConfig.NewTxBuilder()
|
||||
require.NoError(t, txBuilder.SetMsgs([]types.Msg{msg}...))
|
||||
txBuilder.SetMemo(handlerOptions.Memo)
|
||||
txBuilder.SetFeeAmount(types.Coins{types.NewInt64Coin("uatom", 1000)})
|
||||
theTx := txBuilder.GetTx()
|
||||
|
||||
legacySigningData := signing.SignerData{
|
||||
ChainID: handlerOptions.ChainId,
|
||||
Address: handlerOptions.SignerAddress,
|
||||
AccountNumber: handlerOptions.AccNum,
|
||||
Sequence: handlerOptions.AccSeq,
|
||||
}
|
||||
legacySignBz, err := legacyHandler.GetSignBytes(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
legacySigningData, theTx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(legacySignBz), string(signBz))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -687,14 +781,14 @@ func TestDecimalMutation(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
rateBz, _ = encCfg.Amino.MarshalJSON(rates)
|
||||
|
||||
// these assertions show behavior prior to the merge of https://github.com/cosmos/cosmos-sdk/pull/15506
|
||||
// and should be updated to reflect the new behavior once a release of math is made and updated in ./tests/go.mod
|
||||
// require.NotEqual(t, `{"rate":"0","max_rate":"0","max_change_rate":"0"}`, string(rateBz))
|
||||
// require.Equal(t,
|
||||
// `{"rate":"0.000000000000000000","max_rate":"0.000000000000000000","max_change_rate":"0.000000000000000000"}`,
|
||||
// string(rateBz))
|
||||
// prior to the merge of https://github.com/cosmos/cosmos-sdk/pull/15506
|
||||
// gogoproto.Marshal would mutate Decimal fields changing JSON output as shown in the assertions below
|
||||
//require.NotEqual(t, `{"rate":"0","max_rate":"0","max_change_rate":"0"}`, string(rateBz))
|
||||
//require.Equal(t,
|
||||
// `{"rate":"0.000000000000000000","max_rate":"0.000000000000000000","max_change_rate":"0.000000000000000000"}`,
|
||||
// string(rateBz))
|
||||
|
||||
// new behavior
|
||||
// This is no longer the case, new behavior:
|
||||
require.Equal(t, `{"rate":"0","max_rate":"0","max_change_rate":"0"}`, string(rateBz))
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,12 @@ var _ signing.SignModeHandler = signModeLegacyAminoJSONHandler{}
|
||||
// SignModeHandler.
|
||||
type signModeLegacyAminoJSONHandler struct{}
|
||||
|
||||
// NewSignModeLegacyAminoJSONHandler returns a new signModeLegacyAminoJSONHandler.
|
||||
// Note: The public constructor is only used for testing.
|
||||
func NewSignModeLegacyAminoJSONHandler() signing.SignModeHandler {
|
||||
return signModeLegacyAminoJSONHandler{}
|
||||
}
|
||||
|
||||
func (s signModeLegacyAminoJSONHandler) DefaultMode() signingtypes.SignMode {
|
||||
return signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
|
||||
}
|
||||
|
||||
@ -48,3 +48,4 @@ require a `signing.ProtoFileResolver` interface instead of `protodesc.Resolver`.
|
||||
|
||||
* [#15302](https://github.com/cosmos/cosmos-sdk/pull/15302) Add support for a custom registry (e.g. gogo's MergedRegistry) to be plugged into SIGN_MODE_TEXTUAL.
|
||||
* [#15557](https://github.com/cosmos/cosmos-sdk/pull/15557) Implement unknown field filtering.
|
||||
* [#15515](https://github.com/cosmos/cosmos-sdk/pull/15515) Implement SIGN_MODE_LEGACY_AMINO_JSON handler.
|
||||
@ -1,2 +1,3 @@
|
||||
codegen:
|
||||
@(cd internal/testpb; buf generate)
|
||||
@(cd signing/aminojson/internal; make codegen)
|
||||
@ -1,7 +1,6 @@
|
||||
syntax = "proto3";
|
||||
package testpb;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "cosmos/tx/v1beta1/tx.proto";
|
||||
|
||||
@ -103,7 +102,7 @@ message TestVersion1 {
|
||||
TestVersion1 a = 2;
|
||||
TestVersion1 b = 3; // [(gogoproto.nullable) = false] generates invalid recursive structs;
|
||||
repeated TestVersion1 c = 4;
|
||||
repeated TestVersion1 d = 5 [(gogoproto.nullable) = false];
|
||||
repeated TestVersion1 d = 5;
|
||||
oneof sum {
|
||||
int32 e = 6;
|
||||
TestVersion1 f = 7;
|
||||
@ -112,7 +111,7 @@ message TestVersion1 {
|
||||
repeated TestVersion1 h = 9; // [(gogoproto.castrepeated) = "TestVersion1"];
|
||||
// google.protobuf.Timestamp i = 10;
|
||||
// google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true];
|
||||
Customer1 k = 12 [(gogoproto.embed) = true];
|
||||
Customer1 k = 12;
|
||||
}
|
||||
message TestVersion2 {
|
||||
int64 x = 1;
|
||||
@ -128,7 +127,7 @@ message TestVersion2 {
|
||||
repeated TestVersion1 h = 9; // [(gogoproto.castrepeated) = "TestVersion1"];
|
||||
// google.protobuf.Timestamp i = 10;
|
||||
// google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true];
|
||||
Customer1 k = 12 [(gogoproto.embed) = true];
|
||||
Customer1 k = 12;
|
||||
uint64 new_field = 25;
|
||||
}
|
||||
message TestVersion3 {
|
||||
@ -145,7 +144,7 @@ message TestVersion3 {
|
||||
repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"];
|
||||
// google.protobuf.Timestamp i = 10;
|
||||
// google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true];
|
||||
Customer1 k = 12 [(gogoproto.embed) = true];
|
||||
Customer1 k = 12;
|
||||
string non_critical_field = 1031;
|
||||
}
|
||||
|
||||
@ -162,7 +161,7 @@ message TestVersion3LoneOneOfValue {
|
||||
repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"];
|
||||
// google.protobuf.Timestamp i = 10;
|
||||
// google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true];
|
||||
Customer1 k = 12 [(gogoproto.embed) = true];
|
||||
Customer1 k = 12;
|
||||
string non_critical_field = 1031;
|
||||
}
|
||||
|
||||
@ -179,7 +178,7 @@ message TestVersion3LoneNesting {
|
||||
repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"];
|
||||
// google.protobuf.Timestamp i = 10;
|
||||
// google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true];
|
||||
Customer1 k = 12 [(gogoproto.embed) = true];
|
||||
Customer1 k = 12;
|
||||
string non_critical_field = 1031;
|
||||
|
||||
message Inner1 {
|
||||
@ -220,7 +219,7 @@ message TestVersion4LoneNesting {
|
||||
repeated TestVersion1 h = 9; //[(gogoproto.castrepeated) = "TestVersion1"];
|
||||
// google.protobuf.Timestamp i = 10;
|
||||
// google.protobuf.Timestamp j = 11; // [(gogoproto.stdtime) = true];
|
||||
Customer1 k = 12 [(gogoproto.embed) = true];
|
||||
Customer1 k = 12;
|
||||
string non_critical_field = 1031;
|
||||
|
||||
message Inner1 {
|
||||
@ -271,7 +270,7 @@ message TestVersionFD1WithExtraAny {
|
||||
}
|
||||
|
||||
message AnyWithExtra {
|
||||
google.protobuf.Any a = 1 [(gogoproto.embed) = true];
|
||||
google.protobuf.Any a = 1;
|
||||
int64 b = 3;
|
||||
int64 c = 4;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
132
x/tx/signing/aminojson/aminojson.go
Normal file
132
x/tx/signing/aminojson/aminojson.go
Normal file
@ -0,0 +1,132 @@
|
||||
package aminojson
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/reflect/protodesc"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
|
||||
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
|
||||
"cosmossdk.io/x/tx/decode"
|
||||
"cosmossdk.io/x/tx/signing"
|
||||
"cosmossdk.io/x/tx/signing/aminojson/internal/aminojsonpb"
|
||||
)
|
||||
|
||||
// SignModeHandler implements the SIGN_MODE_LEGACY_AMINO_JSON signing mode.
|
||||
type SignModeHandler struct {
|
||||
fileResolver protodesc.Resolver
|
||||
typeResolver protoregistry.MessageTypeResolver
|
||||
encoder Encoder
|
||||
}
|
||||
|
||||
// SignModeHandlerOptions are the options for the SignModeHandler.
|
||||
type SignModeHandlerOptions struct {
|
||||
FileResolver protodesc.Resolver
|
||||
TypeResolver protoregistry.MessageTypeResolver
|
||||
Encoder *Encoder
|
||||
}
|
||||
|
||||
// NewSignModeHandler returns a new SignModeHandler.
|
||||
func NewSignModeHandler(options SignModeHandlerOptions) *SignModeHandler {
|
||||
h := &SignModeHandler{}
|
||||
if options.FileResolver == nil {
|
||||
h.fileResolver = protoregistry.GlobalFiles
|
||||
} else {
|
||||
h.fileResolver = options.FileResolver
|
||||
}
|
||||
if options.TypeResolver == nil {
|
||||
h.typeResolver = protoregistry.GlobalTypes
|
||||
} else {
|
||||
h.typeResolver = options.TypeResolver
|
||||
}
|
||||
if options.Encoder == nil {
|
||||
h.encoder = NewAminoJSON()
|
||||
} else {
|
||||
h.encoder = *options.Encoder
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// Mode implements the Mode method of the SignModeHandler interface.
|
||||
func (h SignModeHandler) Mode() signingv1beta1.SignMode {
|
||||
return signingv1beta1.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
|
||||
}
|
||||
|
||||
// GetSignBytes implements the GetSignBytes method of the SignModeHandler interface.
|
||||
func (h SignModeHandler) GetSignBytes(_ context.Context, signerData signing.SignerData, txData signing.TxData) ([]byte, error) {
|
||||
body := txData.Body
|
||||
_, err := decode.RejectUnknownFields(
|
||||
txData.BodyBytes, body.ProtoReflect().Descriptor(), false, h.fileResolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if (len(body.ExtensionOptions) > 0) || (len(body.NonCriticalExtensionOptions) > 0) {
|
||||
return nil, fmt.Errorf("%s does not support protobuf extension options: invalid request", h.Mode())
|
||||
}
|
||||
|
||||
if signerData.Address == "" {
|
||||
return nil, fmt.Errorf("got empty address in %s handler: invalid request", h.Mode())
|
||||
}
|
||||
|
||||
tip := txData.AuthInfo.Tip
|
||||
if tip != nil && tip.Tipper == "" {
|
||||
return nil, fmt.Errorf("tipper cannot be empty")
|
||||
}
|
||||
isTipper := tip != nil && tip.Tipper == signerData.Address
|
||||
|
||||
// We set a convention that if the tipper signs with LEGACY_AMINO_JSON, then
|
||||
// they sign over empty fees and 0 gas.
|
||||
var fee *aminojsonpb.AminoSignFee
|
||||
if isTipper {
|
||||
fee = &aminojsonpb.AminoSignFee{
|
||||
Amount: nil,
|
||||
Gas: 0,
|
||||
}
|
||||
} else {
|
||||
f := txData.AuthInfo.Fee
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("fee cannot be nil when tipper is not signer")
|
||||
}
|
||||
fee = &aminojsonpb.AminoSignFee{
|
||||
Amount: f.Amount,
|
||||
Gas: f.GasLimit,
|
||||
Payer: f.Payer,
|
||||
Granter: f.Granter,
|
||||
}
|
||||
}
|
||||
|
||||
signDoc := &aminojsonpb.AminoSignDoc{
|
||||
AccountNumber: signerData.AccountNumber,
|
||||
TimeoutHeight: body.TimeoutHeight,
|
||||
ChainId: signerData.ChainId,
|
||||
Sequence: signerData.Sequence,
|
||||
Memo: body.Memo,
|
||||
Msgs: txData.Body.Messages,
|
||||
Fee: fee,
|
||||
}
|
||||
|
||||
bz, err := h.encoder.Marshal(signDoc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sortJSON(bz)
|
||||
}
|
||||
|
||||
// sortJSON sorts the JSON keys of the given JSON encoded byte slice.
|
||||
func sortJSON(toSortJSON []byte) ([]byte, error) {
|
||||
var c interface{}
|
||||
err := json.Unmarshal(toSortJSON, &c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
js, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return js, nil
|
||||
}
|
||||
|
||||
var _ signing.SignModeHandler = (*SignModeHandler)(nil)
|
||||
117
x/tx/signing/aminojson/aminojson_test.go
Normal file
117
x/tx/signing/aminojson/aminojson_test.go
Normal file
@ -0,0 +1,117 @@
|
||||
package aminojson_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
|
||||
"cosmossdk.io/x/tx/signing/aminojson"
|
||||
"cosmossdk.io/x/tx/signing/testutil"
|
||||
)
|
||||
|
||||
func TestAminoJsonSignMode(t *testing.T) {
|
||||
fee := &txv1beta1.Fee{
|
||||
Amount: []*basev1beta1.Coin{{Denom: "uatom", Amount: "1000"}},
|
||||
}
|
||||
handlerOptions := testutil.HandlerArgumentOptions{
|
||||
ChainId: "test-chain",
|
||||
Memo: "sometestmemo",
|
||||
Tip: &txv1beta1.Tip{Tipper: "tipper", Amount: []*basev1beta1.Coin{{Denom: "Tip-token", Amount: "10"}}},
|
||||
Msg: &bankv1beta1.MsgSend{
|
||||
FromAddress: "foo",
|
||||
ToAddress: "bar",
|
||||
Amount: []*basev1beta1.Coin{{Denom: "demon", Amount: "100"}},
|
||||
},
|
||||
AccNum: 1,
|
||||
AccSeq: 2,
|
||||
SignerAddress: "signerAddress",
|
||||
Fee: fee,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
malleate func(opts testutil.HandlerArgumentOptions) testutil.HandlerArgumentOptions
|
||||
error string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
malleate: func(opts testutil.HandlerArgumentOptions) testutil.HandlerArgumentOptions {
|
||||
return opts
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty signer",
|
||||
malleate: func(opts testutil.HandlerArgumentOptions) testutil.HandlerArgumentOptions {
|
||||
opts.SignerAddress = ""
|
||||
return opts
|
||||
},
|
||||
error: "got empty address in SIGN_MODE_LEGACY_AMINO_JSON handler: invalid request",
|
||||
},
|
||||
{
|
||||
name: "nil tip",
|
||||
malleate: func(opts testutil.HandlerArgumentOptions) testutil.HandlerArgumentOptions {
|
||||
opts.Tip = nil
|
||||
return opts
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty tipper",
|
||||
malleate: func(opts testutil.HandlerArgumentOptions) testutil.HandlerArgumentOptions {
|
||||
opts.Tip.Tipper = ""
|
||||
return opts
|
||||
},
|
||||
error: "tipper cannot be empty",
|
||||
},
|
||||
{
|
||||
name: "nil fee",
|
||||
malleate: func(opts testutil.HandlerArgumentOptions) testutil.HandlerArgumentOptions {
|
||||
opts.Tip.Tipper = "tipper"
|
||||
opts.Fee = nil
|
||||
return opts
|
||||
},
|
||||
error: "fee cannot be nil",
|
||||
},
|
||||
{
|
||||
name: "tipper is signer",
|
||||
malleate: func(opts testutil.HandlerArgumentOptions) testutil.HandlerArgumentOptions {
|
||||
opts.Tip.Tipper = opts.SignerAddress
|
||||
return opts
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
opts := tc.malleate(handlerOptions)
|
||||
signerData, txData, err := testutil.MakeHandlerArguments(opts)
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{})
|
||||
_, err = handler.GetSignBytes(context.Background(), signerData, txData)
|
||||
if tc.error != "" {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.error)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNewSignModeHandler(t *testing.T) {
|
||||
handler := aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{})
|
||||
require.NotNil(t, handler)
|
||||
aj := aminojson.NewAminoJSON()
|
||||
handler = aminojson.NewSignModeHandler(aminojson.SignModeHandlerOptions{
|
||||
FileResolver: protoregistry.GlobalFiles,
|
||||
TypeResolver: protoregistry.GlobalTypes,
|
||||
Encoder: &aj,
|
||||
})
|
||||
require.NotNil(t, handler)
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
codegen:
|
||||
@echo "Generating proto files"
|
||||
buf generate
|
||||
@buf generate
|
||||
30
x/tx/signing/aminojson/internal/aminojsonpb/aminojson.proto
Normal file
30
x/tx/signing/aminojson/internal/aminojsonpb/aminojson.proto
Normal file
@ -0,0 +1,30 @@
|
||||
syntax = "proto3";
|
||||
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "amino/amino.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "cosmos/tx/v1beta1/tx.proto";
|
||||
|
||||
// AminoSignFee is the legacy amino json sign mode compatible version of txv1beta1.Fee, and differs from that message
|
||||
// by the name of the Gas field (GasLimit in txv1beta.Fee).
|
||||
// Note: this is only used for signing, see x/tx/signing/aminojson.go for more details.
|
||||
message AminoSignFee {
|
||||
repeated cosmos.base.v1beta1.Coin amount = 1 [(amino.dont_omitempty) = true, (amino.encoding) = "legacy_coins"];
|
||||
uint64 gas = 2 [(amino.dont_omitempty) = true];
|
||||
string payer = 3;
|
||||
string granter = 4;
|
||||
}
|
||||
|
||||
// AminoSignDoc is a message container used to generate the SIGN_MODE_LEGACY_AMINO_JSON sign bytes with proto3 API.
|
||||
// Note: This is only used for generated JSON in signing, see x/tx/signing/aminojson.go for more details.
|
||||
message AminoSignDoc {
|
||||
uint64 account_number = 1;
|
||||
uint64 sequence = 2;
|
||||
uint64 timeout_height = 3;
|
||||
string chain_id = 4;
|
||||
string memo = 5;
|
||||
AminoSignFee fee = 6;
|
||||
repeated google.protobuf.Any msgs = 7;
|
||||
cosmos.tx.v1beta1.Tip tip = 8;
|
||||
}
|
||||
1912
x/tx/signing/aminojson/internal/aminojsonpb/aminojson.pulsar.go
Normal file
1912
x/tx/signing/aminojson/internal/aminojsonpb/aminojson.pulsar.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,3 +5,7 @@ deps:
|
||||
owner: cosmos
|
||||
repository: cosmos-proto
|
||||
commit: 1935555c206d4afb9e94615dfd0fad31
|
||||
- remote: buf.build
|
||||
owner: cosmos
|
||||
repository: gogo-proto
|
||||
commit: 34d970b699f84aa382f3c29773a60836
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
version: v1
|
||||
deps:
|
||||
- buf.build/cosmos/gogo-proto
|
||||
- buf.build/cosmos/cosmos-proto
|
||||
lint:
|
||||
use:
|
||||
|
||||
@ -2,7 +2,9 @@ package std
|
||||
|
||||
import (
|
||||
"cosmossdk.io/x/tx/signing"
|
||||
"cosmossdk.io/x/tx/signing/aminojson"
|
||||
"cosmossdk.io/x/tx/signing/direct"
|
||||
"cosmossdk.io/x/tx/signing/direct_aux"
|
||||
"cosmossdk.io/x/tx/signing/textual"
|
||||
)
|
||||
|
||||
@ -10,6 +12,10 @@ import (
|
||||
type SignModeOptions struct {
|
||||
// Textual are options for SIGN_MODE_TEXTUAL
|
||||
Textual textual.SignModeOptions
|
||||
// DirectAux are options for SIGN_MODE_DIRECT_AUX
|
||||
DirectAux direct_aux.SignModeHandlerOptions
|
||||
// AminoJSON are options for SIGN_MODE_LEGACY_AMINO_JSON
|
||||
AminoJSON aminojson.SignModeHandlerOptions
|
||||
}
|
||||
|
||||
// HandlerMap returns a sign mode handler map that Cosmos SDK apps can use out
|
||||
@ -20,8 +26,17 @@ func (s SignModeOptions) HandlerMap() (*signing.HandlerMap, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
directAux, err := direct_aux.NewSignModeHandler(s.DirectAux)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aminoJSON := aminojson.NewSignModeHandler(s.AminoJSON)
|
||||
|
||||
return signing.NewHandlerMap(
|
||||
direct.SignModeHandler{},
|
||||
txt,
|
||||
directAux,
|
||||
aminoJSON,
|
||||
), nil
|
||||
}
|
||||
|
||||
90
x/tx/signing/testutil/util.go
Normal file
90
x/tx/signing/testutil/util.go
Normal file
@ -0,0 +1,90 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-proto/anyutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"cosmossdk.io/api/cosmos/crypto/secp256k1"
|
||||
signingv1beta1 "cosmossdk.io/api/cosmos/tx/signing/v1beta1"
|
||||
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
|
||||
"cosmossdk.io/x/tx/signing"
|
||||
)
|
||||
|
||||
type HandlerArgumentOptions struct {
|
||||
ChainId string
|
||||
Memo string
|
||||
Msg proto.Message
|
||||
AccNum uint64
|
||||
AccSeq uint64
|
||||
Tip *txv1beta1.Tip
|
||||
Fee *txv1beta1.Fee
|
||||
SignerAddress string
|
||||
}
|
||||
|
||||
func MakeHandlerArguments(options HandlerArgumentOptions) (signing.SignerData, signing.TxData, error) {
|
||||
pk := &secp256k1.PubKey{
|
||||
Key: make([]byte, 256),
|
||||
}
|
||||
anyPk, err := anyutil.New(pk)
|
||||
if err != nil {
|
||||
return signing.SignerData{}, signing.TxData{}, err
|
||||
}
|
||||
|
||||
signerInfo := []*txv1beta1.SignerInfo{
|
||||
{
|
||||
PublicKey: anyPk,
|
||||
ModeInfo: &txv1beta1.ModeInfo{
|
||||
Sum: &txv1beta1.ModeInfo_Single_{
|
||||
Single: &txv1beta1.ModeInfo_Single{
|
||||
Mode: signingv1beta1.SignMode_SIGN_MODE_DIRECT_AUX,
|
||||
},
|
||||
},
|
||||
},
|
||||
Sequence: options.AccSeq,
|
||||
},
|
||||
}
|
||||
|
||||
anyMsg, err := anyutil.New(options.Msg)
|
||||
if err != nil {
|
||||
return signing.SignerData{}, signing.TxData{}, err
|
||||
}
|
||||
|
||||
txBody := &txv1beta1.TxBody{
|
||||
Messages: []*anypb.Any{anyMsg},
|
||||
Memo: options.Memo,
|
||||
}
|
||||
|
||||
authInfo := &txv1beta1.AuthInfo{
|
||||
Fee: options.Fee,
|
||||
Tip: options.Tip,
|
||||
SignerInfos: signerInfo,
|
||||
}
|
||||
|
||||
bodyBz, err := proto.Marshal(txBody)
|
||||
if err != nil {
|
||||
return signing.SignerData{}, signing.TxData{}, err
|
||||
}
|
||||
authInfoBz, err := proto.Marshal(authInfo)
|
||||
if err != nil {
|
||||
return signing.SignerData{}, signing.TxData{}, err
|
||||
}
|
||||
|
||||
txData := signing.TxData{
|
||||
Body: txBody,
|
||||
AuthInfo: authInfo,
|
||||
AuthInfoBytes: authInfoBz,
|
||||
BodyBytes: bodyBz,
|
||||
}
|
||||
|
||||
signerAddress := options.SignerAddress
|
||||
signerData := signing.SignerData{
|
||||
ChainId: options.ChainId,
|
||||
AccountNumber: options.AccNum,
|
||||
Sequence: options.AccSeq,
|
||||
Address: signerAddress,
|
||||
PubKey: anyPk,
|
||||
}
|
||||
|
||||
return signerData, txData, nil
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user