Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add AuxTxBuilder #10455

Merged
merged 23 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions client/tx/aux_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package tx

import (
"github.com/gogo/protobuf/proto"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
)

// AuxTxBuilder is a client-side builder for creating an AuxTx.
type AuxTxBuilder struct {
// msgs is used to store the sdk.Msgs that are added to the
// TxBuilder. It's also added inside body.Messages, because:
// - b.msgs is used for constructing the AMINO sign bz,
// - b.body is used for constructing the DIRECT_AUX sign bz.
msgs []sdk.Msg
body *tx.TxBody
auxSignerData *tx.AuxSignerData
}

func NewAuxTxBuilder() AuxTxBuilder {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
return AuxTxBuilder{}
}

func (b *AuxTxBuilder) SetMemo(memo string) {
b.checkEmptyFields()

b.body.Memo = memo
}

func (b *AuxTxBuilder) SetTimeoutHeight(height uint64) {
b.checkEmptyFields()

b.body.TimeoutHeight = height
}

func (b *AuxTxBuilder) SetMsgs(msgs ...sdk.Msg) error {
anys := make([]*codectypes.Any, len(msgs))
for i, msg := range msgs {
var err error
anys[i], err = codectypes.NewAnyWithValue(msg)
if err != nil {
return err
}
}

b.checkEmptyFields()

b.msgs = msgs
b.body.Messages = anys

return nil
}

func (b *AuxTxBuilder) SetAccountNumber(accNum uint64) {
b.checkEmptyFields()

b.auxSignerData.SignDoc.AccountNumber = accNum
}

func (b *AuxTxBuilder) SetChainID(chainID string) {
b.checkEmptyFields()

b.auxSignerData.SignDoc.ChainId = chainID
}

func (b *AuxTxBuilder) SetSequence(accSeq uint64) {
b.checkEmptyFields()

b.auxSignerData.SignDoc.Sequence = accSeq
}

func (b *AuxTxBuilder) SetPubKey(pk cryptotypes.PubKey) error {
any, err := codectypes.NewAnyWithValue(pk)
if err != nil {
return err
}

b.checkEmptyFields()

b.auxSignerData.SignDoc.PublicKey = any

return nil
}

func (b *AuxTxBuilder) SetSignMode(mode signing.SignMode) error {
switch mode {
case signing.SignMode_SIGN_MODE_DIRECT_AUX, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON:
default:
return sdkerrors.ErrInvalidRequest.Wrapf("AuxTxBuilder can only sign with %s or %s", signing.SignMode_SIGN_MODE_DIRECT_AUX, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
}

b.auxSignerData.Mode = mode

return nil
}

func (b *AuxTxBuilder) SetTip(tip *tx.Tip) {
b.checkEmptyFields()

b.auxSignerData.SignDoc.Tip = tip
}

func (b *AuxTxBuilder) SetSignature(sig []byte) {
if b.auxSignerData == nil {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
b.auxSignerData = &tx.AuxSignerData{}
}

b.auxSignerData.Sig = sig
}

// GetSignBytes returns the builder's sign bytes.
func (b *AuxTxBuilder) GetSignBytes() ([]byte, error) {
auxTx := b.auxSignerData
if auxTx == nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add additional/deeper checks in these initial guards? A call to a single setter that calls checkEmptyFields() will make b.body, b.auxSignerData, and b.auxSignerData.SignDoc non-nil but that doesn't mean they are properly set.

Copy link
Contributor Author

@amaury1093 amaury1093 Nov 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I was thinking was to add some nil-guards here. Then, for actual validation, we call b.auxSignerData.SignDoc.ValidateBasic() and b.auxSignerData.ValidateBasic().

return nil, sdkerrors.ErrLogic.Wrap("aux tx is nil, call setters on AuxTxBuilder first")
}

body := b.body
if body == nil {
return nil, sdkerrors.ErrLogic.Wrap("tx body is nil, call setters on AuxTxBuilder first")
}

sd := auxTx.SignDoc
if sd == nil {
return nil, sdkerrors.ErrLogic.Wrap("sign doc is nil, call setters on AuxTxBuilder first")
}

bodyBz, err := proto.Marshal(body)
if err != nil {
return nil, err
}

sd.BodyBytes = bodyBz

if err := b.auxSignerData.SignDoc.ValidateBasic(); err != nil {
return nil, err
}

var signBz []byte
switch b.auxSignerData.Mode {
case signing.SignMode_SIGN_MODE_DIRECT_AUX:
{

amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
signBz, err = proto.Marshal(b.auxSignerData.SignDoc)
if err != nil {
return nil, err
}
}
case signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON:
{
signBz = legacytx.StdSignBytes(
b.auxSignerData.SignDoc.ChainId, b.auxSignerData.SignDoc.AccountNumber,
b.auxSignerData.SignDoc.Sequence, b.body.TimeoutHeight,
// Aux signer never signs over fee.
// For LEGACY_AMINO_JSON, we use the convention to sign
// over empty fees.
// ref: https://github.com/cosmos/cosmos-sdk/pull/10348
legacytx.StdFee{},
b.msgs, b.body.Memo, b.auxSignerData.SignDoc.Tip,
)
}
default:
return nil, sdkerrors.ErrInvalidRequest.Wrapf("got unknown sign mode %s", b.auxSignerData.Mode)
}

return signBz, nil
}

// GetAuxTx returns the builder's AuxTx.
func (b *AuxTxBuilder) GetAuxTx() (*tx.AuxSignerData, error) {
if err := b.auxSignerData.ValidateBasic(); err != nil {
return nil, err
}

return b.auxSignerData, nil
}

func (b *AuxTxBuilder) checkEmptyFields() {
if b.body == nil {
b.body = &tx.TxBody{}
}

if b.auxSignerData == nil {
b.auxSignerData = &tx.AuxSignerData{}
if b.auxSignerData.SignDoc == nil {
b.auxSignerData.SignDoc = &tx.SignDocDirectAux{}
}
}
}
Loading