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 4 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
149 changes: 149 additions & 0 deletions client/tx/aux_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
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"
)

// AuxTxBuilder is a client-side builder for creating an AuxTx.
type AuxTxBuilder struct {
body *tx.TxBody
auxSignerData *tx.AuxSignerData
}

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.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) 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) {
body := b.body
if body == nil {
return nil, sdkerrors.ErrLogic.Wrap("tx body is nil, call setters on AuxTxBuilder first")
}

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

auxTx := b.auxSignerData
if auxTx == nil {
return nil, sdkerrors.ErrLogic.Wrap("aux tx 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")
}

sd.BodyBytes = bodyBz

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

signBz, err := proto.Marshal(b.auxSignerData.SignDoc)
if err != nil {
return nil, err
}

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{}
}
}
}
20 changes: 20 additions & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@

- [cosmos/tx/v1beta1/tx.proto](#cosmos/tx/v1beta1/tx.proto)
- [AuthInfo](#cosmos.tx.v1beta1.AuthInfo)
- [AuxSignerData](#cosmos.tx.v1beta1.AuxSignerData)
- [Fee](#cosmos.tx.v1beta1.Fee)
- [ModeInfo](#cosmos.tx.v1beta1.ModeInfo)
- [ModeInfo.Multi](#cosmos.tx.v1beta1.ModeInfo.Multi)
Expand Down Expand Up @@ -9474,6 +9475,25 @@ Since: cosmos-sdk 0.45 |



<a name="cosmos.tx.v1beta1.AuxSignerData"></a>

### AuxSignerData
AuxSignerData is the intermediary format that an auxiliary signer (e.g. a
tipper) builds and sends to the fee payer (who will build and broadcast the
actual tx). AuxSignerData is not a valid tx in itself, and will be rejected
by the node if sent directly as-is.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `sign_doc` | [SignDocDirectAux](#cosmos.tx.v1beta1.SignDocDirectAux) | | sign_doc is the SIGN_MOD_DIRECT_AUX sign doc that the auxiliary signer signs. |
| `sig` | [bytes](#bytes) | | sig is the signature of the sign doc. |






<a name="cosmos.tx.v1beta1.Fee"></a>

### Fee
Expand Down
12 changes: 12 additions & 0 deletions proto/cosmos/tx/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,15 @@ message Tip {
// tipper is the address of the account paying for the tip
string tipper = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}

// AuxSignerData is the intermediary format that an auxiliary signer (e.g. a
// tipper) builds and sends to the fee payer (who will build and broadcast the
// actual tx). AuxSignerData is not a valid tx in itself, and will be rejected
// by the node if sent directly as-is.
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
message AuxSignerData {
// sign_doc is the SIGN_MOD_DIRECT_AUX sign doc that the auxiliary signer
// signs.
SignDocDirectAux sign_doc = 1;
// sig is the signature of the sign doc.
bytes sig = 2;
}
49 changes: 49 additions & 0 deletions types/tx/aux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package tx

import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// ValidateBasic performs stateless validation of the sign doc.
func (s *SignDocDirectAux) ValidateBasic() error {
if len(s.BodyBytes) == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("body bytes cannot be empty")
}

if s.PublicKey == nil {
return sdkerrors.ErrInvalidPubKey.Wrap("public key cannot be empty")
}

if s.Tip != nil {
if len(s.Tip.Amount) == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("tip amount cannot be empty")
}

if s.Tip.Tipper == "" {
return sdkerrors.ErrInvalidRequest.Wrap("tipper cannot be empty")
}
}

return nil
}

// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method
func (s *SignDocDirectAux) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
return unpacker.UnpackAny(s.PublicKey, new(cryptotypes.PubKey))
}

// ValidateBasic performs stateless validation of the auxiliary tx.
func (a *AuxTx) ValidateBasic() error {
if len(a.Sig) == 0 {
return sdkerrors.ErrNoSignatures.Wrap("signature cannot be empty")
}

return a.GetSignDoc().ValidateBasic()
}

// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method
func (a *AuxTx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
return a.GetSignDoc().UnpackInterfaces(unpacker)
}
Loading