Update auth cli commands (#6717)

* add utils

* update sign cmd

* update multisign cmd

* update sign batch cmd

* update genutil cmd

* add wrap tx builder

* update gentx cli

* update validate sigs cmd

* fix lint

* add flag reader to cli

* update marshaler for batchscan

* add register query server

* update to master

* remove depricated methods

* fix keyring issue

* update wraptx

* Fix batch scan

* fix register interfaces issue

* update printOutput

* Update Validate Sigs test

* WIP on signature JSON

* Cleanup

* Cleanup

* Test fixes

* Test fixes

* Fixes

* WIP on tests

* Fix gov tests

* fix lint

* fix MultiSign tests

* fix tests

* refactor tests

* Cleanup

* Address review comments

* Update encode

* Test fix

* Rename SignData func

* Fix lint

* Fix lint

* Revert ReadTxCommandFlags

Co-authored-by: Aaron Craelius <aaron@regen.network>
Co-authored-by: Aaron Craelius <aaronc@users.noreply.github.com>
This commit is contained in:
SaReN 2020-07-30 04:03:42 +05:30 committed by GitHub
parent cf078c0c88
commit 72ebafeeca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1986 additions and 537 deletions

View File

@ -195,6 +195,17 @@ func (ctx Context) WithInterfaceRegistry(interfaceRegistry codectypes.InterfaceR
return ctx
}
// PrintString prints the raw string to ctx.Output or os.Stdout
func (ctx Context) PrintString(str string) error {
writer := ctx.Output
if writer == nil {
writer = os.Stdout
}
_, err := writer.Write([]byte(str))
return err
}
// PrintOutput outputs toPrint to the ctx.Output based on ctx.OutputFormat which is
// either text or json. If text, toPrint will be YAML encoded. Otherwise, toPrint
// will be JSON encoded using ctx.JSONMarshaler. An error is returned upon failure.

View File

@ -172,3 +172,14 @@ func (f Factory) WithSimulateAndExecute(sim bool) Factory {
f.simulateAndExecute = sim
return f
}
// SignMode returns the sign mode configured in the Factory
func (f Factory) SignMode() signing.SignMode {
return f.signMode
}
// WithSignMode returns a copy of the Factory with an updated sign mode value.
func (f Factory) WithSignMode(mode signing.SignMode) Factory {
f.signMode = mode
return f
}

View File

@ -64,7 +64,12 @@ func GenerateTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
return err
}
return clientCtx.PrintOutput(tx.GetTx())
json, err := clientCtx.TxConfig.TxJSONEncoder()(tx.GetTx())
if err != nil {
return err
}
return clientCtx.PrintString(fmt.Sprintf("%s\n", json))
}
// BroadcastTx attempts to generate, sign and broadcast a transaction with the

View File

@ -17,6 +17,8 @@ type (
TxDecoder() sdk.TxDecoder
TxJSONEncoder() sdk.TxEncoder
TxJSONDecoder() sdk.TxDecoder
MarshalSignatureJSON([]signingtypes.SignatureV2) ([]byte, error)
UnmarshalSignatureJSON([]byte) ([]signingtypes.SignatureV2, error)
}
// TxConfig defines an interface a client can utilize to generate an

View File

@ -16,7 +16,9 @@ type ProtoCodec struct {
anyUnpacker types.AnyUnpacker
}
func NewProtoCodec(anyUnpacker types.AnyUnpacker) Marshaler {
var _ Marshaler = &ProtoCodec{}
func NewProtoCodec(anyUnpacker types.AnyUnpacker) *ProtoCodec {
return &ProtoCodec{anyUnpacker: anyUnpacker}
}

View File

@ -204,7 +204,7 @@ func mustAny(msg proto.Message) *types.Any {
}
func BenchmarkProtoCodecMarshalBinaryLengthPrefixed(b *testing.B) {
var pCdc = codec.NewProtoCodec(types.NewInterfaceRegistry()).(*codec.ProtoCodec)
var pCdc = codec.NewProtoCodec(types.NewInterfaceRegistry())
var msg = &testdata.HasAnimal{
X: 1000,
Animal: mustAny(&testdata.HasAnimal{

View File

@ -17,6 +17,9 @@ type PubKey interface {
// GetPubKeys returns the crypto.PubKey's nested within the multi-sig PubKey
GetPubKeys() []crypto.PubKey
// GetThreshold returns the threshold number of signatures that must be obtained to verify a signature.
GetThreshold() uint
}
// GetSignBytesFunc defines a function type which returns sign bytes for a given SignMode or an error.

View File

@ -154,3 +154,8 @@ func (pk PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool {
}
return true
}
// GetThreshold implements the PubKey.GetThreshold method
func (pk PubKeyMultisigThreshold) GetThreshold() uint {
return pk.K
}

5
go.sum
View File

@ -168,8 +168,6 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -646,7 +644,6 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20u
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@ -772,7 +769,5 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View File

@ -1,6 +1,8 @@
syntax = "proto3";
package cosmos.tx.signing;
import "cosmos/crypto/crypto.proto";
option go_package = "github.com/cosmos/cosmos-sdk/types/tx/signing";
// SignMode represents a signing mode with its own security guarantees
@ -20,3 +22,49 @@ enum SignMode {
// Amino JSON and will be removed in the future
SIGN_MODE_LEGACY_AMINO_JSON = 127;
}
// SignatureDescriptors wraps multiple SignatureDescriptor's.
message SignatureDescriptors {
// signatures are the signature descriptors
repeated SignatureDescriptor signatures = 1;
}
// SignatureDescriptor is a convenience type which represents the full data for a
// signature including the public key of the signer, signing modes and the signature
// itself. It is primarily used for coordinating signatures between clients.
message SignatureDescriptor {
// public_key is the public key of the signer
cosmos.crypto.PublicKey public_key = 1;
Data data = 2;
// Data represents signature data
message Data {
// sum is the oneof that specifies whether this represents single or multi-signature data
oneof sum {
// single represents a single signer
Single single = 1;
// multi represents a multisig signer
Multi multi = 2;
}
// Single is the signature data for a single signer
message Single {
// mode is the signing mode of the single signer
cosmos.tx.signing.SignMode mode = 1;
// signature is the raw signature bytes
bytes signature = 2;
}
// Multi is the signature data for a multisig public key
message Multi {
// bitarray specifies which keys within the multisig are signing
cosmos.crypto.CompactBitArray bitarray = 1;
// signatures is the signatures of the multi-signature
repeated Data signatures = 2;
}
}
}

View File

@ -12,15 +12,15 @@ import (
// MakeEncodingConfig creates an EncodingConfig for an amino based test configuration.
func MakeEncodingConfig() EncodingConfig {
cdc := codec.New()
amino := codec.New()
interfaceRegistry := types.NewInterfaceRegistry()
marshaler := codec.NewHybridCodec(cdc, interfaceRegistry)
txGen := tx.NewTxConfig(interfaceRegistry, std.DefaultPublicKeyCodec{}, tx.DefaultSignModeHandler())
marshaler := codec.NewHybridCodec(amino, interfaceRegistry)
txGen := tx.NewTxConfig(codec.NewProtoCodec(interfaceRegistry), std.DefaultPublicKeyCodec{}, tx.DefaultSignModeHandler())
return EncodingConfig{
InterfaceRegistry: interfaceRegistry,
Marshaler: marshaler,
TxConfig: txGen,
Amino: cdc,
Amino: amino,
}
}

View File

@ -13,8 +13,6 @@ import (
"testing"
"time"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/stretchr/testify/require"
tmcfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
@ -29,6 +27,7 @@ import (
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
clientkeys "github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"

37
types/tx/sig_desc.pb.go Normal file
View File

@ -0,0 +1,37 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: cosmos/tx/sig_desc.proto
package tx
import (
fmt "fmt"
_ "github.com/cosmos/cosmos-sdk/types/tx/signing"
proto "github.com/gogo/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
func init() { proto.RegisterFile("cosmos/tx/sig_desc.proto", fileDescriptor_d560eb6bd3d62ef7) }
var fileDescriptor_d560eb6bd3d62ef7 = []byte{
// 129 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x48, 0xce, 0x2f, 0xce,
0xcd, 0x2f, 0xd6, 0x2f, 0xa9, 0xd0, 0x2f, 0xce, 0x4c, 0x8f, 0x4f, 0x49, 0x2d, 0x4e, 0xd6, 0x2b,
0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x84, 0xc8, 0xe8, 0x95, 0x54, 0x48, 0xc9, 0xa3, 0x28, 0xca,
0xcb, 0xcc, 0x4b, 0x87, 0xd1, 0x10, 0xb5, 0x4e, 0xf6, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24,
0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78,
0x2c, 0xc7, 0x10, 0xa5, 0x9a, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f,
0x35, 0x05, 0x42, 0xe9, 0x16, 0xa7, 0x64, 0xeb, 0x97, 0x54, 0x16, 0xa4, 0x82, 0x8c, 0x4d, 0x62,
0x03, 0x9b, 0x63, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x86, 0x8a, 0xa4, 0x8a, 0x8f, 0x00, 0x00,
0x00,
}

View File

@ -1,6 +1,10 @@
package signing
import "github.com/tendermint/tendermint/crypto"
import (
"fmt"
"github.com/tendermint/tendermint/crypto"
)
// SignatureV2 is a convenience type that is easier to use in application logic
// than the protobuf SignerInfo's and raw signature bytes. It goes beyond the
@ -15,3 +19,64 @@ type SignatureV2 struct {
// the signatures themselves for either single or multi-signatures.
Data SignatureData
}
// SignatureDataToProto converts a SignatureData to SignatureDescriptor_Data.
// SignatureDescriptor_Data is considered an encoding type whereas SignatureData is used for
// business logic.
func SignatureDataToProto(data SignatureData) *SignatureDescriptor_Data {
switch data := data.(type) {
case *SingleSignatureData:
return &SignatureDescriptor_Data{
Sum: &SignatureDescriptor_Data_Single_{
Single: &SignatureDescriptor_Data_Single{
Mode: data.SignMode,
Signature: data.Signature,
},
},
}
case *MultiSignatureData:
descDatas := make([]*SignatureDescriptor_Data, len(data.Signatures))
for j, d := range data.Signatures {
descDatas[j] = SignatureDataToProto(d)
}
return &SignatureDescriptor_Data{
Sum: &SignatureDescriptor_Data_Multi_{
Multi: &SignatureDescriptor_Data_Multi{
Bitarray: data.BitArray,
Signatures: descDatas,
},
},
}
default:
panic(fmt.Errorf("unexpected case %+v", data))
}
}
// SignatureDataFromProto converts a SignatureDescriptor_Data to SignatureData.
// SignatureDescriptor_Data is considered an encoding type whereas SignatureData is used for
// business logic.
func SignatureDataFromProto(descData *SignatureDescriptor_Data) SignatureData {
switch descData := descData.Sum.(type) {
case *SignatureDescriptor_Data_Single_:
return &SingleSignatureData{
SignMode: descData.Single.Mode,
Signature: descData.Single.Signature,
}
case *SignatureDescriptor_Data_Multi_:
multi := descData.Multi
datas := make([]SignatureData, len(multi.Signatures))
for j, d := range multi.Signatures {
datas[j] = SignatureDataFromProto(d)
}
return &MultiSignatureData{
BitArray: multi.Bitarray,
Signatures: datas,
}
default:
panic(fmt.Errorf("unexpected case %+v", descData))
}
}

File diff suppressed because it is too large Load Diff

View File

@ -14,19 +14,16 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
codec2 "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/network"
sdk "github.com/cosmos/cosmos-sdk/types"
authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
authtest "github.com/cosmos/cosmos-sdk/x/auth/client/testutil"
"github.com/cosmos/cosmos-sdk/x/auth/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
@ -47,7 +44,21 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.cfg = cfg
s.network = network.New(s.T(), cfg)
_, err := s.network.WaitForHeight(1)
kb := s.network.Validators[0].ClientCtx.Keyring
_, _, err := kb.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
s.Require().NoError(err)
account1, _, err := kb.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
s.Require().NoError(err)
account2, _, err := kb.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
s.Require().NoError(err)
multi := multisig.NewPubKeyMultisigThreshold(2, []tmcrypto.PubKey{account1.GetPubKey(), account2.GetPubKey()})
_, err = kb.SaveMultisig("multi", multi)
s.Require().NoError(err)
_, err = s.network.WaitForHeight(1)
s.Require().NoError(err)
}
@ -73,29 +84,23 @@ func (s *IntegrationTestSuite) TestCLIValidateSignatures() {
)
s.Require().NoError(err)
var tx types.StdTx
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res.Bytes(), &tx)
s.Require().NoError(err)
// write unsigned tx to file
unsignedTx, cleanup := testutil.WriteToNewTempFile(s.T(), res.String())
defer cleanup()
res, err = authtest.TxSignExec(val.ClientCtx, val.Address, unsignedTx.Name())
s.Require().NoError(err)
var signedTx types.StdTx
err = val.ClientCtx.JSONMarshaler.UnmarshalJSON(res.Bytes(), &signedTx)
signedTx, err := val.ClientCtx.TxConfig.TxJSONDecoder()(res.Bytes())
s.Require().NoError(err)
signedTxFile, cleanup := testutil.WriteToNewTempFile(s.T(), res.String())
defer cleanup()
txBuilder, err := val.ClientCtx.TxConfig.WrapTxBuilder(signedTx)
res, err = authtest.TxValidateSignaturesExec(val.ClientCtx, signedTxFile.Name())
s.Require().NoError(err)
signedTx.Memo = "MODIFIED STD TX"
bz, err := val.ClientCtx.JSONMarshaler.MarshalJSON(signedTx)
txBuilder.SetMemo("MODIFIED TX")
bz, err := val.ClientCtx.TxConfig.TxJSONEncoder()(txBuilder.GetTx())
s.Require().NoError(err)
modifiedTxFile, cleanup := testutil.WriteToNewTempFile(s.T(), string(bz))
@ -147,28 +152,26 @@ func (s *IntegrationTestSuite) TestCLISignBatch() {
defer cleanup2()
res, err = authtest.TxSignBatchExec(val.ClientCtx, val.Address, malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID))
s.Require().EqualError(err, "cannot parse disfix JSON wrapper: invalid character 'm' looking for beginning of value")
s.Require().Error(err)
// Sign batch malformed tx file signature only.
res, err = authtest.TxSignBatchExec(val.ClientCtx, val.Address, malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--signature-only")
s.Require().EqualError(err, "cannot parse disfix JSON wrapper: invalid character 'm' looking for beginning of value")
s.Require().Error(err)
}
func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
val1 := s.network.Validators[0]
account, _, err := val1.ClientCtx.Keyring.NewMnemonic("newAccount", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
account, err := val1.ClientCtx.Keyring.Key("newAccount")
s.Require().NoError(err)
sendTokens := sdk.TokensFromConsensusPower(10)
sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10))
normalGeneratedTx, err := bankcli.MsgSendExec(
val1.ClientCtx,
val1.Address,
account.GetAddress(),
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sendTokens),
),
sdk.NewCoins(sendTokens),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
@ -176,19 +179,22 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
)
s.Require().NoError(err)
normalGeneratedStdTx := unmarshalStdTx(s.T(), val1.ClientCtx.JSONMarshaler, normalGeneratedTx.String())
s.Require().Equal(normalGeneratedStdTx.Fee.Gas, uint64(flags.DefaultGasLimit))
s.Require().Equal(len(normalGeneratedStdTx.Msgs), 1)
s.Require().Equal(0, len(normalGeneratedStdTx.GetSignatures()))
txCfg := val1.ClientCtx.TxConfig
normalGeneratedStdTx, err := txCfg.TxJSONDecoder()(normalGeneratedTx.Bytes())
s.Require().NoError(err)
txBuilder, err := txCfg.WrapTxBuilder(normalGeneratedStdTx)
s.Require().NoError(err)
s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(flags.DefaultGasLimit))
s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1)
s.Require().Equal(0, len(txBuilder.GetTx().GetSignatures()))
// Test generate sendTx with --gas=$amount
limitedGasGeneratedTx, err := bankcli.MsgSendExec(
val1.ClientCtx,
val1.Address,
account.GetAddress(),
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sendTokens),
),
sdk.NewCoins(sendTokens),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
@ -197,19 +203,28 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
)
s.Require().NoError(err)
limitedGasStdTx := unmarshalStdTx(s.T(), val1.ClientCtx.JSONMarshaler, limitedGasGeneratedTx.String())
s.Require().Equal(limitedGasStdTx.Fee.Gas, uint64(100))
s.Require().Equal(len(limitedGasStdTx.Msgs), 1)
s.Require().Equal(0, len(limitedGasStdTx.GetSignatures()))
limitedGasStdTx, err := txCfg.TxJSONDecoder()(limitedGasGeneratedTx.Bytes())
s.Require().NoError(err)
txBuilder, err = txCfg.WrapTxBuilder(limitedGasStdTx)
s.Require().NoError(err)
s.Require().Equal(txBuilder.GetTx().GetGas(), uint64(100))
s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1)
s.Require().Equal(0, len(txBuilder.GetTx().GetSignatures()))
resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address)
s.Require().NoError(err)
var coins sdk.Coins
err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &coins)
s.Require().NoError(err)
startTokens := coins.AmountOf(s.cfg.BondDenom)
// Test generate sendTx, estimate gas
finalGeneratedTx, err := bankcli.MsgSendExec(
val1.ClientCtx,
val1.Address,
account.GetAddress(),
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sendTokens),
),
sdk.NewCoins(sendTokens),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
@ -218,9 +233,12 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
)
s.Require().NoError(err)
finalStdTx := unmarshalStdTx(s.T(), val1.ClientCtx.JSONMarshaler, finalGeneratedTx.String())
s.Require().Equal(uint64(flags.DefaultGasLimit), finalStdTx.Fee.Gas)
s.Require().Equal(len(finalStdTx.Msgs), 1)
finalStdTx, err := txCfg.TxJSONDecoder()(finalGeneratedTx.Bytes())
s.Require().NoError(err)
txBuilder, err = txCfg.WrapTxBuilder(finalStdTx)
s.Require().NoError(err)
s.Require().Equal(uint64(flags.DefaultGasLimit), txBuilder.GetTx().GetGas())
s.Require().Equal(len(finalStdTx.GetMsgs()), 1)
// Write the output to disk
unsignedTxFile, cleanup := testutil.WriteToNewTempFile(s.T(), finalGeneratedTx.String())
@ -245,11 +263,13 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
// Sign transaction
signedTx, err := authtest.TxSignExec(val1.ClientCtx, val1.Address, unsignedTxFile.Name())
s.Require().NoError(err)
signedFinalTx := unmarshalStdTx(s.T(), val1.ClientCtx.JSONMarshaler, signedTx.String())
s.Require().Equal(len(signedFinalTx.Msgs), 1)
s.Require().Equal(1, len(signedFinalTx.GetSignatures()))
s.Require().Equal(val1.Address.String(), signedFinalTx.GetSigners()[0].String())
signedFinalTx, err := txCfg.TxJSONDecoder()(signedTx.Bytes())
s.Require().NoError(err)
txBuilder, err = val1.ClientCtx.TxConfig.WrapTxBuilder(signedFinalTx)
s.Require().NoError(err)
s.Require().Equal(len(txBuilder.GetTx().GetMsgs()), 1)
s.Require().Equal(1, len(txBuilder.GetTx().GetSignatures()))
s.Require().Equal(val1.Address.String(), txBuilder.GetTx().GetSigners()[0].String())
// Write the output to disk
signedTxFile, cleanup2 := testutil.WriteToNewTempFile(s.T(), signedTx.String())
@ -261,11 +281,9 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
s.Require().True(strings.Contains(res.String(), "[OK]"))
// Ensure foo has right amount of funds
startTokens := sdk.TokensFromConsensusPower(400)
resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address)
resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address)
s.Require().NoError(err)
var coins sdk.Coins
err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &coins)
s.Require().NoError(err)
s.Require().Equal(startTokens, coins.AmountOf(s.cfg.BondDenom))
@ -291,7 +309,7 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &coins)
s.Require().NoError(err)
s.Require().Equal(sendTokens, coins.AmountOf(s.cfg.BondDenom))
s.Require().Equal(sendTokens.Amount, coins.AmountOf(s.cfg.BondDenom))
// Ensure origin account state
resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, val1.Address)
@ -299,11 +317,9 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &coins)
s.Require().NoError(err)
s.Require().Equal(sdk.NewInt(389999990), coins.AmountOf(s.cfg.BondDenom))
}
func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() {
s.T().SkipNow() // TODO check encoding.
val1 := s.network.Validators[0]
codec := codec2.New()
@ -312,14 +328,10 @@ func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() {
val1.ClientCtx.Codec = codec
// Generate 2 accounts and a multisig.
account1, _, err := val1.ClientCtx.Keyring.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
account1, err := val1.ClientCtx.Keyring.Key("newAccount1")
s.Require().NoError(err)
account2, _, err := val1.ClientCtx.Keyring.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
s.Require().NoError(err)
multi := multisig.NewPubKeyMultisigThreshold(2, []tmcrypto.PubKey{account1.GetPubKey(), account2.GetPubKey()})
multisigInfo, err := val1.ClientCtx.Keyring.SaveMultisig("multi", multi)
multisigInfo, err := val1.ClientCtx.Keyring.Key("multi")
s.Require().NoError(err)
// Send coins from validator to multisig.
@ -374,7 +386,7 @@ func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() {
defer cleanup3()
exec, err := authtest.TxValidateSignaturesExec(val1.ClientCtx, multiSigWith1SignatureFile.Name())
s.Require().NoError(err)
s.Require().Error(err)
fmt.Printf("%s", exec)
}
@ -382,15 +394,13 @@ func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() {
func (s *IntegrationTestSuite) TestCLIEncode() {
val1 := s.network.Validators[0]
sendTokens := sdk.TokensFromConsensusPower(10)
sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10))
normalGeneratedTx, err := bankcli.MsgSendExec(
val1.ClientCtx,
val1.Address,
val1.Address,
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sendTokens),
),
sdk.NewCoins(sendTokens),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
@ -402,7 +412,7 @@ func (s *IntegrationTestSuite) TestCLIEncode() {
savedTxFile, cleanup := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String())
defer cleanup()
// Enconde
// Encode
encodeExec, err := authtest.TxEncodeExec(val1.ClientCtx, savedTxFile.Name())
s.Require().NoError(err)
@ -412,12 +422,15 @@ func (s *IntegrationTestSuite) TestCLIEncode() {
decodedTx, err := authtest.TxDecodeExec(val1.ClientCtx, trimmedBase64)
s.Require().NoError(err)
theTx := unmarshalStdTx(s.T(), val1.ClientCtx.JSONMarshaler, decodedTx.String())
s.Require().Equal("deadbeef", theTx.Memo)
txCfg := val1.ClientCtx.TxConfig
theTx, err := txCfg.TxJSONDecoder()(decodedTx.Bytes())
s.Require().NoError(err)
txBuilder, err := val1.ClientCtx.TxConfig.WrapTxBuilder(theTx)
s.Require().NoError(err)
s.Require().Equal("deadbeef", txBuilder.GetTx().GetMemo())
}
func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() {
s.T().SkipNow()
val1 := s.network.Validators[0]
codec := codec2.New()
@ -426,25 +439,30 @@ func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() {
val1.ClientCtx.Codec = codec
// Generate 2 accounts and a multisig.
account1, _, err := val1.ClientCtx.Keyring.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
account1, err := val1.ClientCtx.Keyring.Key("newAccount1")
s.Require().NoError(err)
account2, _, err := val1.ClientCtx.Keyring.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
account2, err := val1.ClientCtx.Keyring.Key("newAccount2")
s.Require().NoError(err)
multi := multisig.NewPubKeyMultisigThreshold(2, []tmcrypto.PubKey{account1.GetPubKey(), account2.GetPubKey()})
multisigInfo, err := val1.ClientCtx.Keyring.SaveMultisig("multi", multi)
multisigInfo, err := val1.ClientCtx.Keyring.Key("multi")
s.Require().NoError(err)
resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress())
s.Require().NoError(err)
var coins sdk.Coins
err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &coins)
s.Require().NoError(err)
intialCoins := coins
// Send coins from validator to multisig.
sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10)
_, err = bankcli.MsgSendExec(
val1.ClientCtx,
val1.Address,
multisigInfo.GetAddress(),
sdk.NewCoins(
sendTokens,
),
sdk.NewCoins(sendTokens),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
@ -454,13 +472,13 @@ func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() {
s.Require().NoError(s.network.WaitForNextBlock())
resp, err := bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress())
resp, err = bankcli.QueryBalancesExec(val1.ClientCtx, multisigInfo.GetAddress())
s.Require().NoError(err)
var coins sdk.Coins
err = val1.ClientCtx.JSONMarshaler.UnmarshalJSON(resp.Bytes(), &coins)
s.Require().NoError(err)
s.Require().Equal(sendTokens.Amount, coins.AmountOf(s.cfg.BondDenom))
diff, _ := coins.SafeSub(intialCoins)
s.Require().Equal(sendTokens.Amount, diff.AmountOf(s.cfg.BondDenom))
// Generate multisig transaction.
multiGeneratedTx, err := bankcli.MsgSendExec(
@ -514,7 +532,6 @@ func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() {
}
func (s *IntegrationTestSuite) TestCLIMultisign() {
s.T().SkipNow()
val1 := s.network.Validators[0]
codec := codec2.New()
@ -523,14 +540,13 @@ func (s *IntegrationTestSuite) TestCLIMultisign() {
val1.ClientCtx.Codec = codec
// Generate 2 accounts and a multisig.
account1, _, err := val1.ClientCtx.Keyring.NewMnemonic("newAccount1", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
account1, err := val1.ClientCtx.Keyring.Key("newAccount1")
s.Require().NoError(err)
account2, _, err := val1.ClientCtx.Keyring.NewMnemonic("newAccount2", keyring.English, sdk.FullFundraiserPath, hd.Secp256k1)
account2, err := val1.ClientCtx.Keyring.Key("newAccount2")
s.Require().NoError(err)
multi := multisig.NewPubKeyMultisigThreshold(2, []tmcrypto.PubKey{account1.GetPubKey(), account2.GetPubKey()})
multisigInfo, err := val1.ClientCtx.Keyring.SaveMultisig("multi", multi)
multisigInfo, err := val1.ClientCtx.Keyring.Key("multi")
s.Require().NoError(err)
// Send coins from validator to multisig.
@ -539,9 +555,7 @@ func (s *IntegrationTestSuite) TestCLIMultisign() {
val1.ClientCtx,
val1.Address,
multisigInfo.GetAddress(),
sdk.NewCoins(
sendTokens,
),
sdk.NewCoins(sendTokens),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
@ -593,8 +607,7 @@ func (s *IntegrationTestSuite) TestCLIMultisign() {
defer cleanup3()
// Does not work in offline mode.
val1.ClientCtx.Offline = true
_, err = authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), sign1File.Name(), sign2File.Name())
_, err = authtest.TxMultiSignExec(val1.ClientCtx, multisigInfo.GetName(), multiGeneratedTxFile.Name(), "--offline", sign1File.Name(), sign2File.Name())
s.Require().EqualError(err, "couldn't verify signature")
val1.ClientCtx.Offline = false
@ -617,7 +630,7 @@ func (s *IntegrationTestSuite) TestCLIMultisign() {
func TestGetBroadcastCommand_OfflineFlag(t *testing.T) {
clientCtx := client.Context{}.WithOffline(true)
clientCtx = clientCtx.WithTxConfig(simappparams.MakeEncodingConfig().TxConfig)
clientCtx = clientCtx.WithTxConfig(simapp.MakeEncodingConfig().TxConfig)
cmd := authcli.GetBroadcastCommand()
_ = testutil.ApplyMockIODiscardOutErr(cmd)
@ -628,7 +641,8 @@ func TestGetBroadcastCommand_OfflineFlag(t *testing.T) {
func TestGetBroadcastCommand_WithoutOfflineFlag(t *testing.T) {
clientCtx := client.Context{}
clientCtx = clientCtx.WithTxConfig(simappparams.MakeEncodingConfig().TxConfig)
txCfg := simapp.MakeEncodingConfig().TxConfig
clientCtx = clientCtx.WithTxConfig(txCfg)
ctx := context.Background()
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
@ -639,9 +653,17 @@ func TestGetBroadcastCommand_WithoutOfflineFlag(t *testing.T) {
t.Cleanup(cleanFunc)
// Create new file with tx
txContents := []byte("{\"type\":\"cosmos-sdk/StdTx\",\"value\":{\"msg\":[{\"type\":\"cosmos-sdk/MsgSend\",\"value\":{\"from_address\":\"cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw\",\"to_address\":\"cosmos1wc8mpr8m3sy3ap3j7fsgqfzx36um05pystems4\",\"amount\":[{\"denom\":\"stake\",\"amount\":\"10000\"}]}}],\"fee\":{\"amount\":[],\"gas\":\"200000\"},\"signatures\":null,\"memo\":\"\"}}")
builder := txCfg.NewTxBuilder()
builder.SetGasLimit(200000)
from, err := sdk.AccAddressFromBech32("cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw")
require.NoError(t, err)
to, err := sdk.AccAddressFromBech32("cosmos1cxlt8kznps92fwu3j6npahx4mjfutydyene2qw")
require.NoError(t, err)
err = builder.SetMsgs(banktypes.NewMsgSend(from, to, sdk.Coins{sdk.NewInt64Coin("stake", 10000)}))
require.NoError(t, err)
txContents, err := txCfg.TxJSONEncoder()(builder.GetTx())
txFileName := filepath.Join(testDir, "tx.json")
err := ioutil.WriteFile(txFileName, txContents, 0644)
err = ioutil.WriteFile(txFileName, txContents, 0644)
require.NoError(t, err)
cmd.SetArgs([]string{txFileName})
@ -654,8 +676,3 @@ func TestGetBroadcastCommand_WithoutOfflineFlag(t *testing.T) {
func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}
func unmarshalStdTx(t require.TestingT, c codec.JSONMarshaler, s string) (stdTx authtypes.StdTx) {
require.Nil(t, c.UnmarshalJSON([]byte(s), &stdTx))
return stdTx
}

View File

@ -3,6 +3,7 @@ package cli
import (
"encoding/base64"
"encoding/hex"
"fmt"
"github.com/spf13/cobra"
@ -37,7 +38,12 @@ func GetDecodeCommand() *cobra.Command {
return err
}
return clientCtx.PrintOutput(tx)
json, err := clientCtx.TxConfig.TxJSONEncoder()(tx)
if err != nil {
return err
}
return clientCtx.PrintString(fmt.Sprintf("%s\n", json))
},
}

View File

@ -2,6 +2,7 @@ package cli
import (
"encoding/base64"
"fmt"
"github.com/spf13/cobra"
@ -10,13 +11,6 @@ import (
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
)
// txEncodeRespStr implements a simple Stringer wrapper for a encoded tx.
type txEncodeRespStr string
func (txr txEncodeRespStr) String() string {
return string(txr)
}
// GetEncodeCommand returns the encode command to take a JSONified transaction and turn it into
// Amino-serialized bytes
func GetEncodeCommand() *cobra.Command {
@ -44,8 +38,7 @@ If you supply a dash (-) argument in place of an input filename, the command rea
// base64 encode the encoded tx bytes
txBytesBase64 := base64.StdEncoding.EncodeToString(txBytes)
response := txEncodeRespStr(txBytesBase64)
return clientCtx.PrintOutput(response)
return clientCtx.PrintString(fmt.Sprintf("%s\n", txBytesBase64))
},
}

View File

@ -23,15 +23,17 @@ func TestGetCommandEncode(t *testing.T) {
authtypes.RegisterCodec(encodingConfig.Amino)
sdk.RegisterCodec(encodingConfig.Amino)
txGen := encodingConfig.TxConfig
txCfg := encodingConfig.TxConfig
// Build a test transaction
fee := authtypes.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)})
stdTx := authtypes.NewStdTx([]sdk.Msg{}, fee, []authtypes.StdSignature{}, "foomemo")
JSONEncoded, err := txGen.TxJSONEncoder()(stdTx)
builder := txCfg.NewTxBuilder()
builder.SetGasLimit(50000)
builder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)})
builder.SetMemo("foomemo")
jsonEncoded, err := txCfg.TxJSONEncoder()(builder.GetTx())
require.NoError(t, err)
txFile, cleanup := testutil.WriteToNewTempFile(t, string(JSONEncoded))
txFile, cleanup := testutil.WriteToNewTempFile(t, string(jsonEncoded))
txFileName := txFile.Name()
t.Cleanup(cleanup)
@ -58,15 +60,17 @@ func TestGetCommandDecode(t *testing.T) {
sdk.RegisterCodec(encodingConfig.Amino)
txGen := encodingConfig.TxConfig
clientCtx = clientCtx.WithTxConfig(txGen)
txCfg := encodingConfig.TxConfig
clientCtx = clientCtx.WithTxConfig(txCfg)
// Build a test transaction
fee := authtypes.NewStdFee(50000, sdk.Coins{sdk.NewInt64Coin("atom", 150)})
stdTx := authtypes.NewStdTx([]sdk.Msg{}, fee, []authtypes.StdSignature{}, "foomemo")
builder := txCfg.NewTxBuilder()
builder.SetGasLimit(50000)
builder.SetFeeAmount(sdk.Coins{sdk.NewInt64Coin("atom", 150)})
builder.SetMemo("foomemo")
// Encode transaction
txBytes, err := clientCtx.TxConfig.TxEncoder()(stdTx)
txBytes, err := clientCtx.TxConfig.TxEncoder()(builder.GetTx())
require.NoError(t, err)
// Convert the transaction into base64 encoded string

View File

@ -11,13 +11,14 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/errors"
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/version"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
@ -60,13 +61,27 @@ recommended to set such parameters manually.
func makeMultiSignCmd() func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) (err error) {
clientCtx := client.GetClientContextFromCmd(cmd)
cdc := clientCtx.Codec
tx, err := authclient.ReadTxFromFile(clientCtx, args[0])
stdTx := tx.(types.StdTx)
clientCtx, err = client.ReadTxCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
parsedTx, err := authclient.ReadTxFromFile(clientCtx, args[0])
if err != nil {
return
}
txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags())
if txFactory.SignMode() == signingtypes.SignMode_SIGN_MODE_UNSPECIFIED {
txFactory = txFactory.WithSignMode(signingtypes.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
}
txCfg := clientCtx.TxConfig
txBuilder, err := txCfg.WrapTxBuilder(parsedTx)
if err != nil {
return err
}
backend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend)
inBuf := bufio.NewReader(cmd.InOrStdin())
@ -85,63 +100,53 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) error {
multisigPub := multisigInfo.GetPubKey().(multisig.PubKeyMultisigThreshold)
multisigSig := multisig.NewMultisig(len(multisigPub.PubKeys))
txBldr, err := types.NewTxBuilderFromFlags(inBuf, cmd.Flags(), clientCtx.HomeDir)
if err != nil {
return errors.Wrap(err, "error creating tx builder from flags")
}
if !clientCtx.Offline {
accnum, seq, err := types.NewAccountRetriever(clientCtx.JSONMarshaler).GetAccountNumberSequence(clientCtx, multisigInfo.GetAddress())
if err != nil {
return err
}
txBldr = txBldr.WithAccountNumber(accnum).WithSequence(seq)
txFactory = txFactory.WithAccountNumber(accnum).WithSequence(seq)
}
// read each signature and add it to the multisig if valid
for i := 2; i < len(args); i++ {
stdSig, err := readAndUnmarshalStdSignature(cdc, args[i])
sigs, err := unmarshalSignatureJSON(clientCtx, args[i])
if err != nil {
return err
}
// Validate each signature
sigBytes := types.StdSignBytes(
txBldr.ChainID(), txBldr.AccountNumber(), txBldr.Sequence(),
stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(),
)
if ok := stdSig.GetPubKey().VerifyBytes(sigBytes, stdSig.Signature); !ok {
return fmt.Errorf("couldn't verify signature")
signingData := signing.SignerData{
ChainID: txFactory.ChainID(),
AccountNumber: txFactory.AccountNumber(),
AccountSequence: txFactory.Sequence(),
}
sigV2, err := types.StdSignatureToSignatureV2(cdc, stdSig)
if err != nil {
return nil
}
for _, sig := range sigs {
err = signing.VerifySignature(sig.PubKey, signingData, sig.Data, txCfg.SignModeHandler(), txBuilder.GetTx())
if err != nil {
return fmt.Errorf("couldn't verify signature")
}
if err := multisig.AddSignatureV2(multisigSig, sigV2, multisigPub.PubKeys); err != nil {
return err
if err := multisig.AddSignatureV2(multisigSig, sig, multisigPub.PubKeys); err != nil {
return err
}
}
}
sigBz, err := types.SignatureDataToAminoSignature(cdc, multisigSig)
sigV2 := signingtypes.SignatureV2{
PubKey: multisigPub,
Data: multisigSig,
}
err = txBuilder.SetSignatures(sigV2)
if err != nil {
return err
}
newStdSig := types.StdSignature{Signature: sigBz, PubKey: multisigPub.Bytes()} //nolint:staticcheck
newTx := types.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, []types.StdSignature{newStdSig}, stdTx.GetMemo()) //nolint:staticcheck
var json []byte
sigOnly, _ := cmd.Flags().GetBool(flagSigOnly)
if sigOnly {
json, err = cdc.MarshalJSON(newTx.Signatures[0])
} else {
json, err = cdc.MarshalJSON(newTx)
}
json, err := marshalSignatureJSON(txCfg, txBuilder, sigOnly)
if err != nil {
return err
}
@ -158,18 +163,14 @@ func makeMultiSignCmd() func(cmd *cobra.Command, args []string) error {
}
defer fp.Close()
fmt.Fprintf(fp, "%s\n", json)
return
return clientCtx.PrintString(fmt.Sprintf("%s\n", json))
}
}
func readAndUnmarshalStdSignature(cdc *codec.Codec, filename string) (stdSig types.StdSignature, err error) { //nolint:staticcheck
func unmarshalSignatureJSON(clientCtx client.Context, filename string) (sigs []signingtypes.SignatureV2, err error) {
var bytes []byte
if bytes, err = ioutil.ReadFile(filename); err != nil {
return
}
if err = cdc.UnmarshalJSON(bytes, &stdSig); err != nil {
return
}
return
return clientCtx.TxConfig.UnmarshalSignatureJSON(bytes)
}

View File

@ -1,7 +1,6 @@
package cli
import (
"bufio"
"fmt"
"os"
@ -9,10 +8,10 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
const (
@ -61,12 +60,13 @@ account key. It implies --signature-only.
func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
txBldr, err := types.NewTxBuilderFromFlags(bufio.NewReader(cmd.InOrStdin()), cmd.Flags(), clientCtx.HomeDir)
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags())
txCfg := clientCtx.TxConfig
generateSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly)
var (
@ -97,34 +97,34 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error {
return err
}
}
scanner := authclient.NewBatchScanner(txCfg, infile)
scanner := authclient.NewBatchScanner(clientCtx.JSONMarshaler, infile)
for sequence := txBldr.Sequence(); scanner.Scan(); sequence++ {
var stdTx types.StdTx
unsignedStdTx := scanner.StdTx()
txBldr = txBldr.WithSequence(sequence)
for sequence := txFactory.Sequence(); scanner.Scan(); sequence++ {
unsignedStdTx := scanner.Tx()
txFactory = txFactory.WithSequence(sequence)
txBuilder, err := txCfg.WrapTxBuilder(unsignedStdTx)
if err != nil {
return err
}
if multisigAddr.Empty() {
from, _ := cmd.Flags().GetString(flags.FlagFrom)
_, fromName, err := client.GetFromFields(txBldr.Keybase(), from, clientCtx.GenerateOnly)
_, fromName, err := client.GetFromFields(txFactory.Keybase(), from, clientCtx.GenerateOnly)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
stdTx, err = authclient.SignStdTx(txBldr, clientCtx, fromName, unsignedStdTx, false, true)
err = authclient.SignTx(txFactory, clientCtx, fromName, txBuilder, true)
if err != nil {
return err
}
} else {
stdTx, err = authclient.SignStdTxWithSignerAddress(txBldr, clientCtx, multisigAddr, clientCtx.GetFromName(), unsignedStdTx, true)
if err != nil {
return err
}
err = authclient.SignTxWithSignerAddress(txFactory, clientCtx, multisigAddr, clientCtx.GetFromName(), txBuilder, true)
}
json, err := getSignatureJSON(clientCtx.JSONMarshaler, stdTx, generateSignatureOnly)
if err != nil {
return err
}
json, err := marshalSignatureJSON(txCfg, txBuilder, generateSignatureOnly)
if err != nil {
return err
}
@ -206,20 +206,31 @@ func preSignCmd(cmd *cobra.Command, _ []string) {
func makeSignCmd() func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
clientCtx, txBldr, tx, err := readStdTxAndInitContexts(clientCtx, cmd, args[0])
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags())
clientCtx, txF, newTx, err := readTxAndInitContexts(clientCtx, cmd, args[0])
if err != nil {
return err
}
if txF.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED {
txF = txF.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
}
txCfg := clientCtx.TxConfig
txBuilder, err := txCfg.WrapTxBuilder(newTx)
if err != nil {
return err
}
stdTx := tx.(types.StdTx)
// if --signature-only is on, then override --append
var newTx types.StdTx
generateSignatureOnly, _ := cmd.Flags().GetBool(flagSigOnly)
multisigAddrStr, _ := cmd.Flags().GetString(flagMultisig)
from, _ := cmd.Flags().GetString(flags.FlagFrom)
_, fromName, err := client.GetFromFields(txBldr.Keybase(), from, clientCtx.GenerateOnly)
_, fromName, err := client.GetFromFields(txFactory.Keybase(), from, clientCtx.GenerateOnly)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
@ -232,23 +243,22 @@ func makeSignCmd() func(cmd *cobra.Command, args []string) error {
return err
}
newTx, err = authclient.SignStdTxWithSignerAddress(
txBldr, clientCtx, multisigAddr, fromName, stdTx, clientCtx.Offline,
err = authclient.SignTxWithSignerAddress(
txF, clientCtx, multisigAddr, fromName, txBuilder, clientCtx.Offline,
)
generateSignatureOnly = true
} else {
append, _ := cmd.Flags().GetBool(flagAppend)
appendSig := append && !generateSignatureOnly
newTx, err = authclient.SignStdTx(txBldr, clientCtx, fromName, stdTx, appendSig, clientCtx.Offline)
if err != nil {
return err
flagAppend, _ := cmd.Flags().GetBool(flagAppend)
appendSig := flagAppend && !generateSignatureOnly
if appendSig {
err = authclient.SignTx(txF, clientCtx, clientCtx.GetFromName(), txBuilder, clientCtx.Offline)
}
}
if err != nil {
return err
}
json, err := getSignatureJSON(clientCtx.JSONMarshaler, newTx, generateSignatureOnly)
json, err := marshalSignatureJSON(txCfg, txBuilder, generateSignatureOnly)
if err != nil {
return err
}
@ -265,15 +275,20 @@ func makeSignCmd() func(cmd *cobra.Command, args []string) error {
}
defer fp.Close()
fmt.Fprintf(fp, "%s\n", json)
return nil
return clientCtx.PrintString(fmt.Sprintf("%s\n", json))
}
}
func getSignatureJSON(cdc codec.JSONMarshaler, newTx types.StdTx, generateSignatureOnly bool) ([]byte, error) {
func marshalSignatureJSON(txConfig client.TxConfig, txBldr client.TxBuilder, generateSignatureOnly bool) ([]byte, error) {
parsedTx := txBldr.GetTx()
if generateSignatureOnly {
return cdc.MarshalJSON(newTx.Signatures[0])
sigs, err := parsedTx.GetSignaturesV2()
if err != nil {
return nil, err
}
return txConfig.MarshalSignatureJSON(sigs)
}
return cdc.MarshalJSON(newTx)
return txConfig.TxJSONEncoder()(parsedTx)
}

View File

@ -1,18 +1,16 @@
package cli
import (
"bufio"
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
"github.com/cosmos/cosmos-sdk/x/auth/types"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
)
func GetValidateSignaturesCommand() *cobra.Command {
@ -41,11 +39,15 @@ transaction will be not be performed as that will require RPC communication with
func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
clientCtx, txBldr, tx, err := readStdTxAndInitContexts(clientCtx, cmd, args[0])
clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags())
if err != nil {
return err
}
clientCtx, txBldr, stdTx, err := readTxAndInitContexts(clientCtx, cmd, args[0])
if err != nil {
return err
}
stdTx := tx.(types.StdTx)
if !printAndValidateSigs(cmd, clientCtx, txBldr.ChainID(), stdTx, clientCtx.Offline) {
return fmt.Errorf("signatures validation failed")
@ -59,17 +61,22 @@ func makeValidateSignaturesCmd() func(cmd *cobra.Command, args []string) error {
// expected signers. In addition, if offline has not been supplied, the signature is
// verified over the transaction sign bytes. Returns false if the validation fails.
func printAndValidateSigs(
cmd *cobra.Command, clientCtx client.Context, chainID string, stdTx types.StdTx, offline bool,
cmd *cobra.Command, clientCtx client.Context, chainID string, tx sdk.Tx, offline bool,
) bool {
cmd.Println("Signers:")
signers := stdTx.GetSigners()
sigTx := tx.(authsigning.SigVerifiableTx)
signModeHandler := clientCtx.TxConfig.SignModeHandler()
cmd.Println("Signers:")
signers := sigTx.GetSigners()
for i, signer := range signers {
cmd.Printf(" %v: %v\n", i, signer.String())
}
success := true
sigs := stdTx.Signatures
sigs, err := sigTx.GetSignaturesV2()
if err != nil {
panic(err)
}
cmd.Println("")
cmd.Println("Signatures:")
@ -79,9 +86,10 @@ func printAndValidateSigs(
for i, sig := range sigs {
var (
pubKey = sig.PubKey
multiSigHeader string
multiSigMsg string
sigAddr = sdk.AccAddress(sig.GetPubKey().Address())
sigAddr = sdk.AccAddress(pubKey.Address())
sigSanity = "OK"
)
@ -93,40 +101,21 @@ func printAndValidateSigs(
// Validate the actual signature over the transaction bytes since we can
// reach out to a full node to query accounts.
if !offline && success {
acc, err := types.NewAccountRetriever(clientCtx.JSONMarshaler).GetAccount(clientCtx, sigAddr)
accNum, accSeq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, sigAddr)
if err != nil {
cmd.Printf("failed to get account: %s\n", sigAddr)
return false
}
sigBytes := types.StdSignBytes(
chainID, acc.GetAccountNumber(), acc.GetSequence(),
stdTx.Fee, stdTx.GetMsgs(), stdTx.GetMemo(),
)
if ok := sig.GetPubKey().VerifyBytes(sigBytes, sig.Signature); !ok {
sigSanity = "ERROR: signature invalid"
success = false
signingData := authsigning.SignerData{
ChainID: chainID,
AccountNumber: accNum,
AccountSequence: accSeq,
}
}
multiPK, ok := sig.GetPubKey().(multisig.PubKeyMultisigThreshold)
if ok {
var multiSig multisig.AminoMultisignature
clientCtx.Codec.MustUnmarshalBinaryBare(sig.Signature, &multiSig)
var b strings.Builder
b.WriteString("\n MultiSig Signatures:\n")
for i := 0; i < multiSig.BitArray.Count(); i++ {
if multiSig.BitArray.GetIndex(i) {
addr := sdk.AccAddress(multiPK.PubKeys[i].Address().Bytes())
b.WriteString(fmt.Sprintf(" %d: %s (weight: %d)\n", i, addr, 1))
}
err = authsigning.VerifySignature(pubKey, signingData, sig.Data, signModeHandler, sigTx)
if err != nil {
return false
}
multiSigHeader = fmt.Sprintf(" [multisig threshold: %d/%d]", multiPK.K, len(multiPK.PubKeys))
multiSigMsg = b.String()
}
cmd.Printf(" %d: %s\t\t\t[%s]%s%s\n", i, sigAddr.String(), sigSanity, multiSigHeader, multiSigMsg)
@ -137,21 +126,13 @@ func printAndValidateSigs(
return success
}
func readStdTxAndInitContexts(clientCtx client.Context, cmd *cobra.Command, filename string) (
client.Context, types.TxBuilder, sdk.Tx, error,
) {
func readTxAndInitContexts(clientCtx client.Context, cmd *cobra.Command, filename string) (client.Context, tx.Factory, sdk.Tx, error) {
stdTx, err := authclient.ReadTxFromFile(clientCtx, filename)
if err != nil {
return client.Context{}, types.TxBuilder{}, types.StdTx{}, err
return clientCtx, tx.Factory{}, nil, err
}
inBuf := bufio.NewReader(cmd.InOrStdin())
clientCtx = clientCtx.WithInput(inBuf)
txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags())
txBldr, err := types.NewTxBuilderFromFlags(inBuf, cmd.Flags(), clientCtx.HomeDir)
if err != nil {
return client.Context{}, types.TxBuilder{}, types.StdTx{}, err
}
return clientCtx, txBldr, stdTx, nil
return clientCtx, txFactory, stdTx, nil
}

View File

@ -10,10 +10,9 @@ import (
"strings"
"github.com/gogo/protobuf/jsonpb"
"github.com/pkg/errors"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/input"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -38,81 +37,6 @@ func (gr GasEstimateResponse) String() string {
return fmt.Sprintf("gas estimate: %d", gr.GasEstimate)
}
// GenerateOrBroadcastMsgs creates a StdTx given a series of messages. If
// the provided context has generate-only enabled, the tx will only be printed
// to STDOUT in a fully offline manner. Otherwise, the tx will be signed and
// broadcasted.
func GenerateOrBroadcastMsgs(clientCtx client.Context, txBldr authtypes.TxBuilder, msgs []sdk.Msg) error {
if clientCtx.GenerateOnly {
return PrintUnsignedStdTx(txBldr, clientCtx, msgs)
}
return CompleteAndBroadcastTxCLI(txBldr, clientCtx, msgs)
}
// CompleteAndBroadcastTxCLI implements a utility function that facilitates
// sending a series of messages in a signed transaction given a TxBuilder and a
// QueryContext. It ensures that the account exists, has a proper number and
// sequence set. In addition, it builds and signs a transaction with the
// supplied messages. Finally, it broadcasts the signed transaction to a node.
func CompleteAndBroadcastTxCLI(txBldr authtypes.TxBuilder, clientCtx client.Context, msgs []sdk.Msg) error {
txBldr, err := PrepareTxBuilder(txBldr, clientCtx)
if err != nil {
return err
}
fromName := clientCtx.GetFromName()
if txBldr.SimulateAndExecute() || clientCtx.Simulate {
txBldr, err = EnrichWithGas(txBldr, clientCtx, msgs)
if err != nil {
return err
}
gasEst := GasEstimateResponse{GasEstimate: txBldr.Gas()}
_, _ = fmt.Fprintf(os.Stderr, "%s\n", gasEst.String())
}
if clientCtx.Simulate {
return nil
}
if !clientCtx.SkipConfirm {
stdSignMsg, err := txBldr.BuildSignMsg(msgs)
if err != nil {
return err
}
json, err := clientCtx.JSONMarshaler.MarshalJSON(stdSignMsg)
if err != nil {
return err
}
_, _ = fmt.Fprintf(os.Stderr, "%s\n\n", json)
buf := bufio.NewReader(os.Stdin)
ok, err := input.GetConfirmation("confirm transaction before signing and broadcasting", buf, os.Stderr)
if err != nil || !ok {
_, _ = fmt.Fprintf(os.Stderr, "%s\n", "cancelled transaction")
return err
}
}
// build and sign the transaction
txBytes, err := txBldr.BuildAndSign(fromName, msgs)
if err != nil {
return err
}
// broadcast to a Tendermint node
res, err := clientCtx.BroadcastTx(txBytes)
if err != nil {
return err
}
return clientCtx.PrintOutput(res)
}
// EnrichWithGas calculates the gas estimate that would be consumed by the
// transaction and set the transaction's respective value accordingly.
func EnrichWithGas(txBldr authtypes.TxBuilder, clientCtx client.Context, msgs []sdk.Msg) (authtypes.TxBuilder, error) {
@ -148,74 +72,52 @@ func CalculateGas(
}
// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
func PrintUnsignedStdTx(txBldr authtypes.TxBuilder, clientCtx client.Context, msgs []sdk.Msg) error {
stdTx, err := buildUnsignedStdTxOffline(txBldr, clientCtx, msgs)
if err != nil {
return err
}
json, err := clientCtx.JSONMarshaler.MarshalJSON(stdTx)
if err != nil {
return err
}
_, _ = fmt.Fprintf(clientCtx.Output, "%s\n", json)
return nil
func PrintUnsignedStdTx(txBldr tx.Factory, clientCtx client.Context, msgs []sdk.Msg) error {
err := tx.GenerateOrBroadcastTxWithFactory(clientCtx, txBldr, msgs...)
return err
}
// SignStdTx appends a signature to a StdTx and returns a copy of it. If appendSig
// SignTx appends a signature to a transaction. If appendSig
// is false, it replaces the signatures already attached with the new signature.
// Don't perform online validation or lookups if offline is true.
func SignStdTx(
txBldr authtypes.TxBuilder, clientCtx client.Context, name string,
stdTx authtypes.StdTx, appendSig bool, offline bool,
) (authtypes.StdTx, error) {
var signedStdTx authtypes.StdTx
info, err := txBldr.Keybase().Key(name)
func SignTx(txFactory tx.Factory, clientCtx client.Context, name string, stdTx client.TxBuilder, offline bool) error {
info, err := txFactory.Keybase().Key(name)
if err != nil {
return signedStdTx, err
return err
}
addr := info.GetPubKey().Address()
// check whether the address is a signer
if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) {
return signedStdTx, fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name)
addr := sdk.AccAddress(info.GetPubKey().Address())
if !isTxSigner(addr, stdTx.GetTx().GetSigners()) {
return fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name)
}
if !offline {
txBldr, err = populateAccountFromState(txBldr, clientCtx, sdk.AccAddress(addr))
txFactory, err = populateAccountFromState(txFactory, clientCtx, addr)
if err != nil {
return signedStdTx, err
return err
}
}
return txBldr.SignStdTx(name, stdTx, appendSig)
return tx.Sign(txFactory, name, stdTx)
}
// SignStdTxWithSignerAddress attaches a signature to a StdTx and returns a copy of a it.
// SignTxWithSignerAddress attaches a signature to a transaction.
// Don't perform online validation or lookups if offline is true, else
// populate account and sequence numbers from a foreign account.
func SignStdTxWithSignerAddress(
txBldr authtypes.TxBuilder, clientCtx client.Context,
addr sdk.AccAddress, name string, stdTx authtypes.StdTx, offline bool,
) (signedStdTx authtypes.StdTx, err error) {
func SignTxWithSignerAddress(txFactory tx.Factory, clientCtx client.Context, addr sdk.AccAddress,
name string, txBuilder client.TxBuilder, offline bool) (err error) {
// check whether the address is a signer
if !isTxSigner(addr, stdTx.GetSigners()) {
return signedStdTx, fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name)
if !isTxSigner(addr, txBuilder.GetTx().GetSigners()) {
return fmt.Errorf("%s: %s", sdkerrors.ErrorInvalidSigner, name)
}
if !offline {
txBldr, err = populateAccountFromState(txBldr, clientCtx, addr)
txFactory, err = populateAccountFromState(txFactory, clientCtx, addr)
if err != nil {
return signedStdTx, err
return err
}
}
return txBldr.SignStdTx(name, stdTx, false)
return tx.Sign(txFactory, name, txBuilder)
}
// Read and decode a StdTx from the given filename. Can pass "-" to read from stdin.
@ -236,21 +138,21 @@ func ReadTxFromFile(ctx client.Context, filename string) (tx sdk.Tx, err error)
}
// NewBatchScanner returns a new BatchScanner to read newline-delimited StdTx transactions from r.
func NewBatchScanner(cdc codec.JSONMarshaler, r io.Reader) *BatchScanner {
return &BatchScanner{Scanner: bufio.NewScanner(r), cdc: cdc}
func NewBatchScanner(cfg client.TxConfig, r io.Reader) *BatchScanner {
return &BatchScanner{Scanner: bufio.NewScanner(r), cfg: cfg}
}
// BatchScanner provides a convenient interface for reading batch data such as a file
// of newline-delimited JSON encoded StdTx.
type BatchScanner struct {
*bufio.Scanner
stdTx authtypes.StdTx
cdc codec.JSONMarshaler
theTx sdk.Tx
cfg client.TxConfig
unmarshalErr error
}
// StdTx returns the most recent StdTx unmarshalled by a call to Scan.
func (bs BatchScanner) StdTx() authtypes.StdTx { return bs.stdTx }
// Tx returns the most recent Tx unmarshalled by a call to Scan.
func (bs BatchScanner) Tx() sdk.Tx { return bs.theTx }
// UnmarshalErr returns the first unmarshalling error that was encountered by the scanner.
func (bs BatchScanner) UnmarshalErr() error { return bs.unmarshalErr }
@ -261,7 +163,9 @@ func (bs *BatchScanner) Scan() bool {
return false
}
if err := bs.cdc.UnmarshalJSON(bs.Bytes(), &bs.stdTx); err != nil && bs.unmarshalErr == nil {
tx, err := bs.cfg.TxJSONDecoder()(bs.Bytes())
bs.theTx = tx
if err != nil && bs.unmarshalErr == nil {
bs.unmarshalErr = err
return false
}
@ -270,8 +174,8 @@ func (bs *BatchScanner) Scan() bool {
}
func populateAccountFromState(
txBldr authtypes.TxBuilder, clientCtx client.Context, addr sdk.AccAddress,
) (authtypes.TxBuilder, error) {
txBldr tx.Factory, clientCtx client.Context, addr sdk.AccAddress,
) (tx.Factory, error) {
num, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, addr)
if err != nil {
@ -316,56 +220,6 @@ func parseQueryResponse(bz []byte) (sdk.SimulationResponse, error) {
return simRes, nil
}
// PrepareTxBuilder populates a TxBuilder in preparation for the build of a Tx.
func PrepareTxBuilder(txBldr authtypes.TxBuilder, clientCtx client.Context) (authtypes.TxBuilder, error) {
from := clientCtx.GetFromAddress()
accGetter := clientCtx.AccountRetriever
if err := accGetter.EnsureExists(clientCtx, from); err != nil {
return txBldr, err
}
txbldrAccNum, txbldrAccSeq := txBldr.AccountNumber(), txBldr.Sequence()
// TODO: (ref #1903) Allow for user supplied account number without
// automatically doing a manual lookup.
if txbldrAccNum == 0 || txbldrAccSeq == 0 {
num, seq, err := accGetter.GetAccountNumberSequence(clientCtx, from)
if err != nil {
return txBldr, err
}
if txbldrAccNum == 0 {
txBldr = txBldr.WithAccountNumber(num)
}
if txbldrAccSeq == 0 {
txBldr = txBldr.WithSequence(seq)
}
}
return txBldr, nil
}
func buildUnsignedStdTxOffline(txBldr authtypes.TxBuilder, clientCtx client.Context, msgs []sdk.Msg) (stdTx authtypes.StdTx, err error) {
if txBldr.SimulateAndExecute() {
if clientCtx.Offline {
return stdTx, errors.New("cannot estimate gas in offline mode")
}
txBldr, err = EnrichWithGas(txBldr, clientCtx, msgs)
if err != nil {
return stdTx, err
}
_, _ = fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas())
}
stdSignMsg, err := txBldr.BuildSignMsg(msgs)
if err != nil {
return stdTx, err
}
return authtypes.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil
}
func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool {
for _, s := range signers {
if bytes.Equal(user.Bytes(), s.Bytes()) {

View File

@ -6,6 +6,7 @@ import (
"strings"
"testing"
"github.com/cosmos/cosmos-sdk/std"
"github.com/cosmos/cosmos-sdk/testutil"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
@ -149,8 +150,12 @@ func TestReadStdTxFromFile(t *testing.T) {
func TestBatchScanner_Scan(t *testing.T) {
t.Parallel()
cdc := codec.New()
sdk.RegisterCodec(cdc)
encodingConfig := simappparams.MakeEncodingConfig()
std.RegisterCodec(encodingConfig.Amino)
txGen := encodingConfig.TxConfig
clientCtx := client.Context{}
clientCtx = clientCtx.WithTxConfig(txGen)
batch1 := `{"msg":[],"fee":{"amount":[{"denom":"atom","amount":"150"}],"gas":"50000"},"signatures":[],"memo":"foomemo"}
{"msg":[],"fee":{"amount":[{"denom":"atom","amount":"150"}],"gas":"10000"},"signatures":[],"memo":"foomemo"}
@ -182,12 +187,12 @@ malformed
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
scanner, i := NewBatchScanner(cdc, strings.NewReader(tt.batch)), 0
scanner, i := NewBatchScanner(clientCtx.TxConfig, strings.NewReader(tt.batch)), 0
for scanner.Scan() {
_ = scanner.StdTx()
_ = scanner.Tx()
i++
}
t.Log(scanner.theTx)
require.Equal(t, tt.wantScannerError, scanner.Err() != nil)
require.Equal(t, tt.wantUnmarshalError, scanner.UnmarshalErr() != nil)
require.Equal(t, tt.numTxs, i)
@ -206,48 +211,6 @@ func compareEncoders(t *testing.T, expected sdk.TxEncoder, actual sdk.TxEncoder)
require.Equal(t, defaultEncoderBytes, encoderBytes)
}
func TestPrepareTxBuilder(t *testing.T) {
cdc := makeCodec()
encodingConfig := simappparams.MakeEncodingConfig()
sdk.RegisterCodec(encodingConfig.Amino)
fromAddr := sdk.AccAddress("test-addr0000000000")
fromAddrStr := fromAddr.String()
var accNum uint64 = 10
var accSeq uint64 = 17
txGen := encodingConfig.TxConfig
clientCtx := client.Context{}
clientCtx = clientCtx.
WithTxConfig(txGen).
WithJSONMarshaler(encodingConfig.Marshaler).
WithAccountRetriever(client.TestAccountRetriever{Accounts: map[string]struct {
Address sdk.AccAddress
Num uint64
Seq uint64
}{
fromAddrStr: {
Address: fromAddr,
Num: accNum,
Seq: accSeq,
},
}}).
WithFromAddress(fromAddr)
bldr := authtypes.NewTxBuilder(
authtypes.DefaultTxEncoder(cdc), 0, 0,
200000, 1.1, false, "test-chain",
"test-builder", sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1))),
sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10000, sdk.Precision))})
bldr, err := PrepareTxBuilder(bldr, clientCtx)
require.NoError(t, err)
require.Equal(t, accNum, bldr.AccountNumber())
require.Equal(t, accSeq, bldr.Sequence())
}
func makeCodec() *codec.Codec {
var cdc = codec.New()
sdk.RegisterCodec(cdc)

View File

@ -3,8 +3,9 @@ package tx
import (
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/client"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
@ -17,17 +18,19 @@ type generator struct {
encoder sdk.TxEncoder
jsonDecoder sdk.TxDecoder
jsonEncoder sdk.TxEncoder
protoCodec *codec.ProtoCodec
}
// NewTxConfig returns a new protobuf TxConfig using the provided Marshaler, PublicKeyCodec and SignModeHandler.
func NewTxConfig(anyUnpacker codectypes.AnyUnpacker, pubkeyCodec types.PublicKeyCodec, signModeHandler signing.SignModeHandler) client.TxConfig {
// NewTxConfig returns a new protobuf TxConfig using the provided ProtoCodec, PublicKeyCodec and SignModeHandler.
func NewTxConfig(protoCodec *codec.ProtoCodec, pubkeyCodec types.PublicKeyCodec, signModeHandler signing.SignModeHandler) client.TxConfig {
return &generator{
pubkeyCodec: pubkeyCodec,
handler: signModeHandler,
decoder: DefaultTxDecoder(anyUnpacker, pubkeyCodec),
decoder: DefaultTxDecoder(protoCodec, pubkeyCodec),
encoder: DefaultTxEncoder(),
jsonDecoder: DefaultJSONTxDecoder(anyUnpacker, pubkeyCodec),
jsonDecoder: DefaultJSONTxDecoder(protoCodec, pubkeyCodec),
jsonEncoder: DefaultJSONTxEncoder(),
protoCodec: protoCodec,
}
}

View File

@ -3,6 +3,8 @@ package tx
import (
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
@ -102,3 +104,50 @@ func decodeMultisignatures(bz []byte) ([][]byte, error) {
}
return multisig.Signatures, nil
}
func (g generator) MarshalSignatureJSON(sigs []signing.SignatureV2) ([]byte, error) {
descs := make([]*signing.SignatureDescriptor, len(sigs))
for i, sig := range sigs {
publicKey, err := g.pubkeyCodec.Encode(sig.PubKey)
if err != nil {
return nil, err
}
descData := signing.SignatureDataToProto(sig.Data)
descs[i] = &signing.SignatureDescriptor{
PublicKey: publicKey,
Data: descData,
}
}
toJSON := &signing.SignatureDescriptors{Signatures: descs}
return codec.ProtoMarshalJSON(toJSON)
}
func (g generator) UnmarshalSignatureJSON(bz []byte) ([]signing.SignatureV2, error) {
var sigDescs signing.SignatureDescriptors
err := g.protoCodec.UnmarshalJSON(bz, &sigDescs)
if err != nil {
return nil, err
}
sigs := make([]signing.SignatureV2, len(sigDescs.Signatures))
for i, desc := range sigDescs.Signatures {
pubKey, err := g.pubkeyCodec.Decode(desc.PublicKey)
if err != nil {
return nil, err
}
data := signing.SignatureDataFromProto(desc.Data)
sigs[i] = signing.SignatureV2{
PubKey: pubKey,
Data: data,
}
}
return sigs, nil
}

View File

@ -7,7 +7,6 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/legacy"
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
@ -46,29 +45,12 @@ func (s *StdTxBuilder) SetSignatures(signatures ...signing.SignatureV2) error {
sigs := make([]StdSignature, len(signatures))
for i, sig := range signatures {
var pubKeyBz []byte
pubKey := sig.PubKey
if pubKey != nil {
pubKeyBz = pubKey.Bytes()
stdSig, err := SignatureV2ToStdSignature(s.cdc, sig)
if err != nil {
return err
}
var (
sigBz []byte
err error
)
if sig.Data != nil {
sigBz, err = SignatureDataToAminoSignature(legacy.Cdc, sig.Data)
if err != nil {
return err
}
}
sigs[i] = StdSignature{
PubKey: pubKeyBz,
Signature: sigBz,
}
sigs[i] = stdSig
}
s.Signatures = sigs
@ -131,6 +113,38 @@ func (s StdTxConfig) TxJSONDecoder() sdk.TxDecoder {
return DefaultJSONTxDecoder(s.Cdc)
}
func (s StdTxConfig) MarshalSignatureJSON(sigs []signing.SignatureV2) ([]byte, error) {
stdSigs := make([]StdSignature, len(sigs))
for i, sig := range sigs {
stdSig, err := SignatureV2ToStdSignature(s.Cdc, sig)
if err != nil {
return nil, err
}
stdSigs[i] = stdSig
}
return s.Cdc.MarshalJSON(stdSigs)
}
func (s StdTxConfig) UnmarshalSignatureJSON(bz []byte) ([]signing.SignatureV2, error) {
var stdSigs []StdSignature
err := s.Cdc.UnmarshalJSON(bz, &stdSigs)
if err != nil {
return nil, err
}
sigs := make([]signing.SignatureV2, len(stdSigs))
for i, stdSig := range stdSigs {
sig, err := StdSignatureToSignatureV2(s.Cdc, stdSig)
if err != nil {
return nil, err
}
sigs[i] = sig
}
return sigs, nil
}
func (s StdTxConfig) SignModeHandler() authsigning.SignModeHandler {
return LegacyAminoJSONHandler{}
}

View File

@ -395,6 +395,33 @@ func StdSignatureToSignatureV2(cdc *codec.Codec, sig StdSignature) (signing.Sign
}, nil
}
// SignatureV2ToStdSignature converts a SignatureV2 to a StdSignature
func SignatureV2ToStdSignature(cdc *codec.Codec, sig signing.SignatureV2) (StdSignature, error) {
var pubKeyBz []byte
pubKey := sig.PubKey
if pubKey != nil {
pubKeyBz = pubKey.Bytes()
}
var (
sigBz []byte
err error
)
if sig.Data != nil {
sigBz, err = SignatureDataToAminoSignature(cdc, sig.Data)
if err != nil {
return StdSignature{}, err
}
}
return StdSignature{
PubKey: pubKeyBz,
Signature: sigBz,
}, nil
}
func pubKeySigToSigData(cdc *codec.Codec, key crypto.PubKey, sig []byte) (signing.SignatureData, error) {
multiPK, ok := key.(multisig.PubKey)
if !ok {

View File

@ -17,6 +17,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"
@ -133,16 +134,17 @@ $ %s gentx my-key-name --home=/path/to/home/dir --keyring-backend=os --chain-id=
return errors.Wrap(err, "failed to validate account in genesis")
}
txBldr, err := authtypes.NewTxBuilderFromFlags(inBuf, cmd.Flags(), clientCtx.HomeDir)
txFactory := tx.NewFactoryCLI(clientCtx, cmd.Flags())
if err != nil {
return errors.Wrap(err, "error creating tx builder")
}
txBldr = txBldr.WithTxEncoder(authclient.GetTxEncoder(clientCtx.Codec))
txGen := clientCtx.TxConfig
txBuilder := txGen.NewTxBuilder()
clientCtx = clientCtx.WithInput(inBuf).WithFromAddress(key.GetAddress())
// create a 'create-validator' message
txBldr, msg, err := cli.BuildCreateValidatorMsg(clientCtx, createValCfg, txBldr, true)
txBldr, msg, err := cli.BuildCreateValidatorMsg(clientCtx, createValCfg, txFactory, true)
if err != nil {
return errors.Wrap(err, "failed to build create-validator message")
}
@ -167,7 +169,7 @@ $ %s gentx my-key-name --home=/path/to/home/dir --keyring-backend=os --chain-id=
}
// sign the transaction and write it to the output file
signedTx, err := authclient.SignStdTx(txBldr, clientCtx, name, stdTx, false, true)
err = authclient.SignTx(txFactory, clientCtx, name, txBuilder, true)
if err != nil {
return errors.Wrap(err, "failed to sign std tx")
}
@ -180,7 +182,7 @@ $ %s gentx my-key-name --home=/path/to/home/dir --keyring-backend=os --chain-id=
}
}
if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil {
if err := writeSignedGenTx(cdc, outputDocument, stdTx); err != nil {
return errors.Wrap(err, "failed to write signed gen tx")
}
@ -219,7 +221,7 @@ func readUnsignedGenTxFile(cdc codec.JSONMarshaler, r io.Reader) (authtypes.StdT
return stdTx, err
}
func writeSignedGenTx(cdc codec.JSONMarshaler, outputDocument string, tx authtypes.StdTx) error {
func writeSignedGenTx(cdc codec.JSONMarshaler, outputDocument string, tx sdk.Tx) error {
outputFile, err := os.OpenFile(outputDocument, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
if err != nil {
return err

View File

@ -109,6 +109,7 @@ func (s *IntegrationTestSuite) TestNewCmdSubmitProposal() {
fmt.Sprintf("--%s=%s", cli.FlagProposal, validPropFile.Name()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
},
false, &sdk.TxResponse{}, 0,
@ -122,6 +123,7 @@ func (s *IntegrationTestSuite) TestNewCmdSubmitProposal() {
fmt.Sprintf("--%s=%s", cli.FlagDeposit, sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(5431)).String()),
fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()),
},
false, &sdk.TxResponse{}, 0,

View File

@ -14,7 +14,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
@ -503,7 +502,7 @@ func PrepareConfigForTxCreateValidator(flagSet *flag.FlagSet, moniker, nodeID, c
}
// BuildCreateValidatorMsg makes a new MsgCreateValidator.
func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorConfig, txBldr authtypes.TxBuilder, generateOnly bool) (authtypes.TxBuilder, sdk.Msg, error) {
func BuildCreateValidatorMsg(clientCtx client.Context, config TxCreateValidatorConfig, txBldr tx.Factory, generateOnly bool) (tx.Factory, sdk.Msg, error) {
amounstStr := config.Amount
amount, err := sdk.ParseCoin(amounstStr)