# Transactions

Transactions are objects created by end-users to trigger state changes in the application.

# Pre-requisite Readings

# Transactions

Transactions are comprised of metadata held in contexts and sdk.Msgs that trigger state changes within a module through the module's Protobuf Msg service.

When users want to interact with an application and make state changes (e.g. sending coins), they create transactions. Each of a transaction's sdk.Msg must be signed using the private key associated with the appropriate account(s), before the transaction is broadcasted to the network. A transaction must then be included in a block, validated, and approved by the network through the consensus process. To read more about the lifecycle of a transaction, click here.

# Type Definition

Transaction objects are SDK types that implement the Tx interface

Copy // Tx defines the interface a transaction must fulfill. Tx interface { // Gets the all the transaction's messages. GetMsgs() []Msg // ValidateBasic does a simple and lightweight validation check that doesn't // require access to any other information. ValidateBasic() error }

It contains the following methods:

  • GetMsgs: unwraps the transaction and returns a list of contained sdk.Msgs - one transaction may have one or multiple messages, which are defined by module developers.
  • ValidateBasic: lightweight, stateless checks used by ABCI messages CheckTx and DeliverTx to make sure transactions are not invalid. For example, the auth (opens new window) module's StdTx ValidateBasic function checks that its transactions are signed by the correct number of signers and that the fees do not exceed what the user's maximum. Note that this function is to be distinct from sdk.Msg ValidateBasic methods, which perform basic validity checks on messages only. When runTx is checking a transaction created from the auth (opens new window) module, it first runs ValidateBasic on each message, then runs the auth module AnteHandler which calls ValidateBasic for the transaction itself.

As a developer, you should rarely manipulate Tx directly, as Tx is really an intermediate type used for transaction generation. Instead, developers should prefer the TxBuilder interface, which you can learn more about below.

# Signing Transactions

Every message in a transaction must be signed by the addresses specified by its GetSigners. The SDK currently allows signing transactions in two different ways.

# SIGN_MODE_DIRECT (preferred)

The most used implementation of the Tx interface is the Protobuf Tx message, which is used in SIGN_MODE_DIRECT:

Copy // Tx is the standard type used for broadcasting transactions. message Tx { // body is the processable content of the transaction TxBody body = 1; // auth_info is the authorization related content of the transaction, // specifically signers, signer modes and fee AuthInfo auth_info = 2; // signatures is a list of signatures that matches the length and order of // AuthInfo's signer_infos to allow connecting signature meta information like // public key and signing mode by position. repeated bytes signatures = 3; }

Because Protobuf serialization is not deterministic, the SDK uses an additional TxRaw type to denote the pinned bytes over which a transaction is signed. Any user can generate a valid body and auth_info for a transaction, and serialize these two messages using Protobuf. TxRaw then pins the user's exact binary representation of body and auth_info, called respectively body_bytes and auth_info_bytes. The document that is signed by all signers of the transaction is SignDoc (deterministically serialized using ADR-027):

Copy // SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. message SignDoc { // body_bytes is protobuf serialization of a TxBody that matches the // representation in TxRaw. bytes body_bytes = 1; // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the // representation in TxRaw. bytes auth_info_bytes = 2; // chain_id is the unique identifier of the chain this transaction targets. // It prevents signed transactions from being used on another chain by an // attacker string chain_id = 3; // account_number is the account number of the account in state uint64 account_number = 4; }

Once signed by all signers, the body_bytes, auth_info_bytes and signatures are gathered into TxRaw, whose serialized bytes are broadcasted over the network.

# SIGN_MODE_LEGACY_AMINO_JSON

The legacy implemention of the Tx interface is the StdTx struct from x/auth:

Copy // StdTx is the legacy transaction format for wrapping a Msg with Fee and Signatures. // It only works with Amino, please prefer the new protobuf Tx in types/tx. // NOTE: the first signature is the fee payer (Signatures must not be nil). // Deprecated type StdTx struct { Msgs []sdk.Msg `json:"msg" yaml:"msg"` Fee StdFee `json:"fee" yaml:"fee"` Signatures []StdSignature `json:"signatures" yaml:"signatures"` Memo string `json:"memo" yaml:"memo"` TimeoutHeight uint64 `json:"timeout_height" yaml:"timeout_height"` }

The document signed by all signers is StdSignDoc:

Copy // StdSignDoc is replay-prevention structure. // It includes the result of msg.GetSignBytes(), // as well as the ChainID (prevent cross chain replay) // and the Sequence numbers for each signature (prevent // inchain replay and enforce tx ordering per account). type StdSignDoc struct { AccountNumber uint64 `json:"account_number" yaml:"account_number"` Sequence uint64 `json:"sequence" yaml:"sequence"` TimeoutHeight uint64 `json:"timeout_height,omitempty" yaml:"timeout_height"` ChainID string `json:"chain_id" yaml:"chain_id"` Memo string `json:"memo" yaml:"memo"` Fee json.RawMessage `json:"fee" yaml:"fee"` Msgs []json.RawMessage `json:"msgs" yaml:"msgs"` }

which is encoded into bytes using Amino JSON. Once all signatures are gathered into StdTx, StdTx is serialized using Amino JSON, and these bytes are broadcasted over the network.

# Other Sign Modes

Other sign modes, most notably SIGN_MODE_TEXTUAL, are being discussed. If you wish to learn more about them, please refer to ADR-020.

# Transaction Process

The process of an end-user sending a transaction is:

  • decide on the messages to put into the transaction,
  • generate the transaction using the SDK's TxBuilder,
  • broadcast the transaction using one of the available interfaces.

The next paragraphs will describe each of these components, in this order.

# Messages

Module sdk.Msgs are not to be confused with ABCI Messages (opens new window) which define interactions between the Tendermint and application layers.

Messages (or sdk.Msgs) are module-specific objects that trigger state transitions within the scope of the module they belong to. Module developers define the messages for their module by adding methods to the Protobuf Msg service, and also implement the corresponding MsgServer.

Each sdk.Msgs is related to exactly one Protobuf Msg service RPC, defined inside each module's tx.proto file. An SKD app router automatically maps every sdk.Msg to a corresponding RPC. Protobuf generates a MsgServer interface for each module Msg service, and the module developer needs to implement this interface. This design puts more responsibility on module developers, allowing application developers to reuse common functionalities without having to implement state transition logic repetitively.

To learn more about Protobuf Msg services and how to implement MsgServer, click here.

While messages contain the information for state transition logic, a transaction's other metadata and relevant information are stored in the TxBuilder and Context.

# Transaction Generation

The TxBuilder interface contains data closely related with the generation of transactions, which an end-user can freely set to generate the desired transaction:

Copy // TxBuilder defines an interface which an application-defined concrete transaction // type must implement. Namely, it must be able to set messages, generate // signatures, and provide canonical bytes to sign over. The transaction must // also know how to encode itself. TxBuilder interface { GetTx() signing.Tx SetMsgs(msgs ...sdk.Msg) error SetSignatures(signatures ...signingtypes.SignatureV2) error SetMemo(memo string) SetFeeAmount(amount sdk.Coins) SetGasLimit(limit uint64) SetTimeoutHeight(height uint64) }

  • Msgs, the array of messages included in the transaction.
  • GasLimit, option chosen by the users for how to calculate how much gas they will need to pay.
  • Memo, a note or comment to send with the transaction.
  • FeeAmount, the maximum amount the user is willing to pay in fees.
  • TimeoutHeight, block height until which the transaction is valid.
  • Signatures, the array of signatures from all signers of the transaction.

As there are currently two sign modes for signing transactions, there are also two implementations of TxBuilder:

However, the two implementation of TxBuilder should be hidden away from end-users, as they should prefer using the overarching TxConfig interface:

Copy // TxConfig defines an interface a client can utilize to generate an // application-defined concrete transaction type. The type returned must // implement TxBuilder. TxConfig interface { TxEncodingConfig NewTxBuilder() TxBuilder WrapTxBuilder(sdk.Tx) (TxBuilder, error) SignModeHandler() signing.SignModeHandler }

TxConfig is an app-wide configuration for managing transactions. Most importantly, it holds the information about whether to sign each transaction with SIGN_MODE_DIRECT or SIGN_MODE_LEGACY_AMINO_JSON. By calling txBuilder := txConfig.NewTxBuilder(), a new TxBuilder will be created with the appropriate sign mode.

Once TxBuilder is correctly populated with the setters exposed above, TxConfig will also take care of correctly encoding the bytes (again, either using SIGN_MODE_DIRECT or SIGN_MODE_LEGACY_AMINO_JSON). Here's a pseudo-code snippet of how to generate and encode a transaction, using the TxEncoder() method:

Copy txBuilder := txConfig.NewTxBuilder() txBuilder.SetMsgs(...) // and other setters on txBuilder bz, err := txConfig.TxEncoder()(txBuilder.GetTx()) // bz are bytes to be broadcasted over the network

# Broadcasting the Transaction

Once the transaction bytes are generated, there are currently three ways of broadcasting it.

# CLI

Application developers create entrypoints to the application by creating a command-line interface, gRPC and/or REST interface, typically found in the application's ./cmd folder. These interfaces allow users to interact with the application through command-line.

For the command-line interface, module developers create subcommands to add as children to the application top-level transaction command TxCmd. CLI commands actually bundle all the steps of transaction processing into one simple command: creating messages, generating transactions and broadcasting. For concrete examples, see the Interacting with a Node section. An example transaction made using CLI looks like:

Copy simd tx send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake

# gRPC

gRPC (opens new window) is introduced in Cosmos SDK 0.40 as the main component for the SDK's RPC layer. The principal usage of gRPC is in the context of modules' Query services. However, the SDK also exposes a few other module-agnostic gRPC services, one of them being the Tx service:

Copy syntax = "proto3"; package cosmos.tx.v1beta1; import "google/api/annotations.proto"; import "cosmos/base/abci/v1beta1/abci.proto"; import "cosmos/tx/v1beta1/tx.proto"; option go_package = "github.com/cosmos/cosmos-sdk/types/tx"; // Service defines a gRPC service for interacting with transactions. service Service { // Simulate simulates executing a transaction for estimating gas usage. rpc Simulate(SimulateRequest) returns (SimulateResponse) { option (google.api.http).post = "/cosmos/tx/v1beta1/simulate"; } // GetTx fetches a tx by hash. rpc GetTx(GetTxRequest) returns (GetTxResponse) { option (google.api.http).get = "/cosmos/tx/v1beta1/tx/{hash}"; } } // SimulateRequest is the request type for the Service.Simulate // RPC method. message SimulateRequest { // tx is the transaction to simulate. cosmos.tx.v1beta1.Tx tx = 1; } // SimulateResponse is the response type for the // Service.SimulateRPC method. message SimulateResponse { // gas_info is the information about gas used in the simulation. cosmos.base.abci.v1beta1.GasInfo gas_info = 1; // result is the result of the simulation. cosmos.base.abci.v1beta1.Result result = 2; } // GetTx is the request type for the Service.GetTx // RPC method. message GetTxRequest { // hash is the tx hash to query, encoded as a hex string. string hash = 1; } // GetTxResponse is the response type for the Service.GetTx method. message GetTxResponse { // tx is the queried transaction. cosmos.tx.v1beta1.Tx tx = 1; }

The Tx service exposes a handful of utility functions, such as simulating a transaction or querying a transaction, and also one method to broadcast transactions.

Examples of broadcasting and simulating a transaction are shown here.

# REST

Each gRPC method has its corresponding REST endpoint, generated using gRPC-gateway (opens new window). Therefore, instead of using gRPC, you can also use HTTP to broadcast the same transaction, on the POST /cosmos/tx/v1beta1/txs endpoint.

An example can be seen here

# Tendermint RPC

The three methods presented above are actually higher abstractions over the Tendermint RPC /broadcast_tx_{async,sync,commit} endpoints, documented here (opens new window). This means that you can use the Tendermint RPC endpoints directly to broadcast the transaction, if you wish so.

# Next

Learn about the context