465 lines
13 KiB
Markdown
465 lines
13 KiB
Markdown
The tx package provides a robust set of tools for building, signing, and managing transactions in a Cosmos SDK-based blockchain application.
|
|
|
|
## Overview
|
|
|
|
This package includes several key components:
|
|
|
|
1. Transaction Factory
|
|
2. Transaction Config
|
|
3. Transaction Encoder/Decoder
|
|
4. Signature Handling
|
|
|
|
## Architecture
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[Client] --> B[Factory]
|
|
B --> D[TxConfig]
|
|
D --> E[TxEncodingConfig]
|
|
D --> F[TxSigningConfig]
|
|
B --> G[Tx]
|
|
G --> H[Encoder]
|
|
G --> I[Decoder]
|
|
F --> J[SignModeHandler]
|
|
F --> K[SigningContext]
|
|
B --> L[AuxTxBuilder]
|
|
```
|
|
|
|
## Key Components
|
|
|
|
### TxConfig
|
|
|
|
`TxConfig` provides configuration for transaction handling, including:
|
|
|
|
- Encoding and decoding
|
|
- Sign mode handling
|
|
- Signature JSON marshaling/unmarshaling
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class TxConfig {
|
|
<<interface>>
|
|
TxEncodingConfig
|
|
TxSigningConfig
|
|
}
|
|
|
|
class TxEncodingConfig {
|
|
<<interface>>
|
|
TxEncoder() txEncoder
|
|
TxDecoder() txDecoder
|
|
TxJSONEncoder() txEncoder
|
|
TxJSONDecoder() txDecoder
|
|
Decoder() Decoder
|
|
}
|
|
|
|
class TxSigningConfig {
|
|
<<interface>>
|
|
SignModeHandler() *signing.HandlerMap
|
|
SigningContext() *signing.Context
|
|
MarshalSignatureJSON([]Signature) ([]byte, error)
|
|
UnmarshalSignatureJSON([]byte) ([]Signature, error)
|
|
}
|
|
|
|
class txConfig {
|
|
TxEncodingConfig
|
|
TxSigningConfig
|
|
}
|
|
|
|
class defaultEncodingConfig {
|
|
cdc codec.BinaryCodec
|
|
decoder Decoder
|
|
TxEncoder() txEncoder
|
|
TxDecoder() txDecoder
|
|
TxJSONEncoder() txEncoder
|
|
TxJSONDecoder() txDecoder
|
|
}
|
|
|
|
class defaultTxSigningConfig {
|
|
signingCtx *signing.Context
|
|
handlerMap *signing.HandlerMap
|
|
cdc codec.BinaryCodec
|
|
SignModeHandler() *signing.HandlerMap
|
|
SigningContext() *signing.Context
|
|
MarshalSignatureJSON([]Signature) ([]byte, error)
|
|
UnmarshalSignatureJSON([]byte) ([]Signature, error)
|
|
}
|
|
|
|
TxConfig <|-- txConfig
|
|
TxEncodingConfig <|.. defaultEncodingConfig
|
|
TxSigningConfig <|.. defaultTxSigningConfig
|
|
txConfig *-- defaultEncodingConfig
|
|
txConfig *-- defaultTxSigningConfig
|
|
```
|
|
|
|
### Factory
|
|
|
|
The `Factory` is the main entry point for creating and managing transactions. It handles:
|
|
|
|
- Account preparation
|
|
- Gas calculation
|
|
- Unsigned transaction building
|
|
- Transaction signing
|
|
- Transaction simulation
|
|
- Transaction broadcasting
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class Factory {
|
|
keybase keyring.Keyring
|
|
cdc codec.BinaryCodec
|
|
accountRetriever account.AccountRetriever
|
|
ac address.Codec
|
|
conn gogogrpc.ClientConn
|
|
txConfig TxConfig
|
|
txParams TxParameters
|
|
tx txState
|
|
|
|
NewFactory(keybase, cdc, accRetriever, txConfig, ac, conn, parameters) Factory
|
|
Prepare() error
|
|
BuildUnsignedTx(msgs ...transaction.Msg) error
|
|
BuildsSignedTx(ctx context.Context, msgs ...transaction.Msg) (Tx, error)
|
|
calculateGas(msgs ...transaction.Msg) error
|
|
Simulate(msgs ...transaction.Msg) (*apitx.SimulateResponse, uint64, error)
|
|
UnsignedTxString(msgs ...transaction.Msg) (string, error)
|
|
BuildSimTx(msgs ...transaction.Msg) ([]byte, error)
|
|
sign(ctx context.Context, overwriteSig bool) (Tx, error)
|
|
WithGas(gas uint64)
|
|
WithSequence(sequence uint64)
|
|
WithAccountNumber(accnum uint64)
|
|
getTx() (Tx, error)
|
|
getFee() (*apitx.Fee, error)
|
|
getSigningTxData() (signing.TxData, error)
|
|
setSignatures(...Signature) error
|
|
}
|
|
|
|
class TxParameters {
|
|
<<struct>>
|
|
chainID string
|
|
AccountConfig
|
|
GasConfig
|
|
FeeConfig
|
|
SignModeConfig
|
|
TimeoutConfig
|
|
MemoConfig
|
|
}
|
|
|
|
class TxConfig {
|
|
<<interface>>
|
|
}
|
|
|
|
class Tx {
|
|
<<interface>>
|
|
}
|
|
|
|
class txState {
|
|
<<struct>>
|
|
msgs []transaction.Msg
|
|
memo string
|
|
fees []*base.Coin
|
|
gasLimit uint64
|
|
feeGranter []byte
|
|
feePayer []byte
|
|
timeoutHeight uint64
|
|
unordered bool
|
|
timeoutTimestamp uint64
|
|
signatures []Signature
|
|
signerInfos []*apitx.SignerInfo
|
|
}
|
|
|
|
Factory *-- TxParameters
|
|
Factory *-- TxConfig
|
|
Factory *-- txState
|
|
Factory ..> Tx : creates
|
|
```
|
|
|
|
### Encoder/Decoder
|
|
|
|
The package includes functions for encoding and decoding transactions in both binary and JSON formats.
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class Decoder {
|
|
<<interface>>
|
|
Decode(txBytes []byte) (*txdecode.DecodedTx, error)
|
|
}
|
|
|
|
class txDecoder {
|
|
<<function>>
|
|
decode(txBytes []byte) (Tx, error)
|
|
}
|
|
|
|
class txEncoder {
|
|
<<function>>
|
|
encode(tx Tx) ([]byte, error)
|
|
}
|
|
|
|
class EncoderUtils {
|
|
<<utility>>
|
|
decodeTx(cdc codec.BinaryCodec, decoder Decoder) txDecoder
|
|
encodeTx(tx Tx) ([]byte, error)
|
|
decodeJsonTx(cdc codec.BinaryCodec, decoder Decoder) txDecoder
|
|
encodeJsonTx(tx Tx) ([]byte, error)
|
|
protoTxBytes(tx *txv1beta1.Tx) ([]byte, error)
|
|
}
|
|
|
|
class MarshalOptions {
|
|
<<utility>>
|
|
Deterministic bool
|
|
}
|
|
|
|
class JSONMarshalOptions {
|
|
<<utility>>
|
|
Indent string
|
|
UseProtoNames bool
|
|
UseEnumNumbers bool
|
|
}
|
|
|
|
Decoder <.. EncoderUtils : uses
|
|
txDecoder <.. EncoderUtils : creates
|
|
txEncoder <.. EncoderUtils : implements
|
|
EncoderUtils ..> MarshalOptions : uses
|
|
EncoderUtils ..> JSONMarshalOptions : uses
|
|
```
|
|
|
|
### Sequence Diagrams
|
|
|
|
#### Generate Aux Signer Data
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User
|
|
participant GenerateOrBroadcastTxCLI
|
|
participant generateAuxSignerData
|
|
participant makeAuxSignerData
|
|
participant AuxTxBuilder
|
|
participant ctx.PrintProto
|
|
|
|
User->>GenerateOrBroadcastTxCLI: Call with isAux flag
|
|
GenerateOrBroadcastTxCLI->>generateAuxSignerData: Call
|
|
|
|
generateAuxSignerData->>makeAuxSignerData: Call
|
|
makeAuxSignerData->>AuxTxBuilder: NewAuxTxBuilder()
|
|
|
|
makeAuxSignerData->>AuxTxBuilder: SetAddress(f.txParams.fromAddress)
|
|
|
|
alt f.txParams.offline
|
|
makeAuxSignerData->>AuxTxBuilder: SetAccountNumber(f.AccountNumber())
|
|
makeAuxSignerData->>AuxTxBuilder: SetSequence(f.Sequence())
|
|
else
|
|
makeAuxSignerData->>f.accountRetriever: GetAccountNumberSequence()
|
|
makeAuxSignerData->>AuxTxBuilder: SetAccountNumber(accNum)
|
|
makeAuxSignerData->>AuxTxBuilder: SetSequence(seq)
|
|
end
|
|
|
|
makeAuxSignerData->>AuxTxBuilder: SetMsgs(msgs...)
|
|
makeAuxSignerData->>AuxTxBuilder: SetSignMode(f.SignMode())
|
|
|
|
makeAuxSignerData->>f.keybase: GetPubKey(f.txParams.fromName)
|
|
makeAuxSignerData->>AuxTxBuilder: SetPubKey(pubKey)
|
|
|
|
makeAuxSignerData->>AuxTxBuilder: SetChainID(f.txParams.chainID)
|
|
makeAuxSignerData->>AuxTxBuilder: GetSignBytes()
|
|
|
|
makeAuxSignerData->>f.keybase: Sign(f.txParams.fromName, signBz, f.SignMode())
|
|
makeAuxSignerData->>AuxTxBuilder: SetSignature(sig)
|
|
|
|
makeAuxSignerData->>AuxTxBuilder: GetAuxSignerData()
|
|
AuxTxBuilder-->>makeAuxSignerData: Return AuxSignerData
|
|
makeAuxSignerData-->>generateAuxSignerData: Return AuxSignerData
|
|
|
|
generateAuxSignerData->>ctx.PrintProto: Print AuxSignerData
|
|
ctx.PrintProto-->>GenerateOrBroadcastTxCLI: Return result
|
|
GenerateOrBroadcastTxCLI-->>User: Return result
|
|
```
|
|
|
|
#### Generate Only
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User
|
|
participant GenerateOrBroadcastTxCLI
|
|
participant generateOnly
|
|
participant Factory
|
|
participant ctx.PrintString
|
|
|
|
User->>GenerateOrBroadcastTxCLI: Call with generateOnly flag
|
|
GenerateOrBroadcastTxCLI->>generateOnly: Call
|
|
|
|
generateOnly->>Factory: Prepare()
|
|
alt Error in Prepare
|
|
Factory-->>generateOnly: Return error
|
|
generateOnly-->>GenerateOrBroadcastTxCLI: Return error
|
|
GenerateOrBroadcastTxCLI-->>User: Return error
|
|
end
|
|
|
|
generateOnly->>Factory: UnsignedTxString(msgs...)
|
|
Factory->>Factory: BuildUnsignedTx(msgs...)
|
|
Factory->>Factory: setMsgs(msgs...)
|
|
Factory->>Factory: setMemo(f.txParams.memo)
|
|
Factory->>Factory: setFees(f.txParams.gasPrices)
|
|
Factory->>Factory: setGasLimit(f.txParams.gas)
|
|
Factory->>Factory: setFeeGranter(f.txParams.feeGranter)
|
|
Factory->>Factory: setFeePayer(f.txParams.feePayer)
|
|
Factory->>Factory: setTimeoutHeight(f.txParams.timeoutHeight)
|
|
|
|
Factory->>Factory: getTx()
|
|
Factory->>Factory: txConfig.TxJSONEncoder()
|
|
Factory->>Factory: encoder(tx)
|
|
|
|
Factory-->>generateOnly: Return unsigned tx string
|
|
generateOnly->>ctx.PrintString: Print unsigned tx string
|
|
ctx.PrintString-->>generateOnly: Return result
|
|
generateOnly-->>GenerateOrBroadcastTxCLI: Return result
|
|
GenerateOrBroadcastTxCLI-->>User: Return result
|
|
```
|
|
|
|
#### DryRun
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User
|
|
participant GenerateOrBroadcastTxCLI
|
|
participant dryRun
|
|
participant Factory
|
|
participant os.Stderr
|
|
|
|
User->>GenerateOrBroadcastTxCLI: Call with dryRun flag
|
|
GenerateOrBroadcastTxCLI->>dryRun: Call
|
|
|
|
dryRun->>Factory: Prepare()
|
|
alt Error in Prepare
|
|
Factory-->>dryRun: Return error
|
|
dryRun-->>GenerateOrBroadcastTxCLI: Return error
|
|
GenerateOrBroadcastTxCLI-->>User: Return error
|
|
end
|
|
|
|
dryRun->>Factory: Simulate(msgs...)
|
|
Factory->>Factory: BuildSimTx(msgs...)
|
|
Factory->>Factory: BuildUnsignedTx(msgs...)
|
|
Factory->>Factory: getSimPK()
|
|
Factory->>Factory: getSimSignatureData(pk)
|
|
Factory->>Factory: setSignatures(sig)
|
|
Factory->>Factory: getTx()
|
|
Factory->>Factory: txConfig.TxEncoder()(tx)
|
|
|
|
Factory->>ServiceClient: Simulate(context.Background(), &apitx.SimulateRequest{})
|
|
ServiceClient->>Factory: Return result
|
|
|
|
Factory-->>dryRun: Return (simulation, gas, error)
|
|
alt Error in Simulate
|
|
dryRun-->>GenerateOrBroadcastTxCLI: Return error
|
|
GenerateOrBroadcastTxCLI-->>User: Return error
|
|
end
|
|
|
|
dryRun->>os.Stderr: Fprintf(GasEstimateResponse{GasEstimate: gas})
|
|
os.Stderr-->>dryRun: Return result
|
|
dryRun-->>GenerateOrBroadcastTxCLI: Return result
|
|
GenerateOrBroadcastTxCLI-->>User: Return result
|
|
```
|
|
|
|
#### Generate and Broadcast Tx
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant User
|
|
participant GenerateOrBroadcastTxCLI
|
|
participant BroadcastTx
|
|
participant Factory
|
|
participant clientCtx
|
|
|
|
User->>GenerateOrBroadcastTxCLI: Call
|
|
GenerateOrBroadcastTxCLI->>BroadcastTx: Call
|
|
|
|
BroadcastTx->>Factory: Prepare()
|
|
alt Error in Prepare
|
|
Factory-->>BroadcastTx: Return error
|
|
BroadcastTx-->>GenerateOrBroadcastTxCLI: Return error
|
|
GenerateOrBroadcastTxCLI-->>User: Return error
|
|
end
|
|
|
|
alt SimulateAndExecute is true
|
|
BroadcastTx->>Factory: calculateGas(msgs...)
|
|
Factory->>Factory: Simulate(msgs...)
|
|
Factory->>Factory: WithGas(adjusted)
|
|
end
|
|
|
|
BroadcastTx->>Factory: BuildUnsignedTx(msgs...)
|
|
Factory->>Factory: setMsgs(msgs...)
|
|
Factory->>Factory: setMemo(f.txParams.memo)
|
|
Factory->>Factory: setFees(f.txParams.gasPrices)
|
|
Factory->>Factory: setGasLimit(f.txParams.gas)
|
|
Factory->>Factory: setFeeGranter(f.txParams.feeGranter)
|
|
Factory->>Factory: setFeePayer(f.txParams.feePayer)
|
|
Factory->>Factory: setTimeoutHeight(f.txParams.timeoutHeight)
|
|
|
|
alt !clientCtx.SkipConfirm
|
|
BroadcastTx->>Factory: getTx()
|
|
BroadcastTx->>Factory: txConfig.TxJSONEncoder()
|
|
BroadcastTx->>clientCtx: PrintRaw(txBytes)
|
|
BroadcastTx->>clientCtx: Input.GetConfirmation()
|
|
alt Not confirmed
|
|
BroadcastTx-->>GenerateOrBroadcastTxCLI: Return error
|
|
GenerateOrBroadcastTxCLI-->>User: Return error
|
|
end
|
|
end
|
|
|
|
BroadcastTx->>Factory: BuildsSignedTx(ctx, msgs...)
|
|
Factory->>Factory: sign(ctx, true)
|
|
Factory->>Factory: keybase.GetPubKey(fromName)
|
|
Factory->>Factory: getSignBytesAdapter()
|
|
Factory->>Factory: keybase.Sign(fromName, bytesToSign, signMode)
|
|
Factory->>Factory: setSignatures(sig)
|
|
Factory->>Factory: getTx()
|
|
|
|
BroadcastTx->>Factory: txConfig.TxEncoder()
|
|
BroadcastTx->>clientCtx: BroadcastTx(txBytes)
|
|
|
|
alt Error in BroadcastTx
|
|
clientCtx-->>BroadcastTx: Return error
|
|
BroadcastTx-->>GenerateOrBroadcastTxCLI: Return error
|
|
GenerateOrBroadcastTxCLI-->>User: Return error
|
|
end
|
|
|
|
BroadcastTx->>clientCtx: OutputTx(res)
|
|
clientCtx-->>BroadcastTx: Return result
|
|
BroadcastTx-->>GenerateOrBroadcastTxCLI: Return result
|
|
GenerateOrBroadcastTxCLI-->>User: Return result
|
|
```
|
|
|
|
## Usage
|
|
|
|
To use the `tx` package, typically you would:
|
|
|
|
1. Create a `Factory`
|
|
2. Simulate the transaction (optional)
|
|
3. Build a signed transaction
|
|
4. Encode the transaction
|
|
5. Broadcast the transaction
|
|
|
|
Here's a simplified example:
|
|
|
|
```go
|
|
// Create a Factory
|
|
factory, err := NewFactory(keybase, cdc, accountRetriever, txConfig, addressCodec, conn, txParameters)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Simulate the transaction (optional)
|
|
simRes, gas, err := factory.Simulate(msgs...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
factory.WithGas(gas)
|
|
|
|
// Build a signed transaction
|
|
signedTx, err := factory.BuildsSignedTx(context.Background(), msgs...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Encode the transaction
|
|
txBytes, err := factory.txConfig.TxEncoder()(signedTx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Broadcast the transaction
|
|
// (This step depends on your specific client implementation)
|
|
``` |