Enviando Transações
Na cadeia CrossFI, você pode enviar, assinar e transmitir transações das seguintes maneiras:
Usando o CLI
A melhor maneira de enviar transações é usando o CLI, como vimos na página anterior ao interagir com um nó. Por exemplo, executando o seguinte comando
crossfid tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000mpx
executará as seguintes etapas:
- gerar uma transação com uma
Msg
(x/bank
'sMsgSend
), e imprimir a transação gerada no console. - pedir confirmação do usuário para enviar a transação da conta
$MY_VALIDATOR_ADDRESS
. - buscar
$MY_VALIDATOR_ADDRESS
do keyring. Isto é possível porque configuramos o keyring do CLI em uma etapa anterior. - assinar a transação gerada com a conta do keyring.
- transmitir a transação assinada para a rede. Isto é possível porque o CLI conecta-se ao endpoint RPC do nó Tendermint.
O CLI agrupa todas as etapas necessárias em uma experiência de usuário fácil de usar. No entanto, é possível executar todas as etapas individualmente também.
Gerando uma Transação
Gerar uma transação pode ser feito simplesmente adicionando a flag --generate-only
em qualquer comando tx
, por exemplo:
crossfid tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000mpx --generate-only
Isso exibirá a transação não assinada como JSON no console. Também podemos salvar a transação não assinada em um arquivo (para ser passada entre os signatários mais facilmente) adicionando > unsigned_tx.json
ao comando acima.
Assinando uma Transação
Assinar uma transação usando o CLI requer que a transação não assinada seja salva em um arquivo. Vamos supor que a transação não assinada esteja em um arquivo chamado unsigned_tx.json
no diretório atual (veja o parágrafo anterior para saber como fazer isso). Em seguida, basta executar o seguinte comando:
crossfid tx sign unsigned_tx.json --from $MY_VALIDATOR_ADDRESS
Este comando decodificará a transação não assinada e a assinará com SIGN_MODE_DIRECT
com a chave de $MY_VALIDATOR_ADDRESS
, que já configuramos no keyring. A transação assinada será exibida como JSON no console e, como acima, podemos salvá-la em um arquivo adicionando > signed_tx.json
.
Algumas flags úteis para considerar no comando tx sign
:
--sign-mode
: você pode usaramino-json
para assinar a transação usandoSIGN_MODE_LEGACY_AMINO_JSON
,--offline
: assinar em modo offline. Isso significa que o comandotx sign
não se conecta ao nó para recuperar o número da conta do signatário e a sequência, ambos necessários para assinar. Neste caso, você deve fornecer manualmente as flags--account-number
e--sequence
. Isso é útil para assinatura offline, ou seja, assinatura em um ambiente seguro que não tem acesso à internet.
Assinatura com Múltiplos Signatários
Observe que assinar uma transação com vários signatários ou com uma conta multisig, onde pelo menos um signatário usa SIGN_MODE_DIRECT
, ainda não é possível. Você pode seguir esta questão do Github para mais informações.
Assinatura com múltiplos signatários é feita com o comando tx multisign
. Este comando presume que todos os signatários usam SIGN_MODE_LEGACY_AMINO_JSON
. O fluxo é similar ao fluxo do comando tx sign
, mas em vez de assinar um arquivo de transação não assinada, cada signatário assina o arquivo assinado pelo(s) signatário(s) anterior(es).
O comando tx multisign
anexará assinaturas às transações existentes. É importante que os signatários assinem a transação na mesma ordem dada pela transação, que é recuperável usando o método GetSigners()
.
Por exemplo, começando com o unsigned_tx.json
, e assumindo que a transação tem 4 signatários, executaríamos:
# Deixe o signatário1 assinar o tx não assinado.
crossfid tx multisign unsigned_tx.json signer_key_1 > partial_tx_1.json
# Agora o signatário1 enviará o partial_tx_1.json para o signatário2.
# O signatário2 anexa sua assinatura:
crossfid tx multisign partial_tx_1.json signer_key_2 > partial_tx_2.json
# O signatário2 envia o arquivo partial_tx_2.json para o signatário3, e o signatário3 pode anexar sua assinatura:
crossfid tx multisign partial_tx_2.json signer_key_3 > partial_tx_3.json
Transmitindo uma Transação
A transmissão de uma transação é feita usando o seguinte comando:
crossfid tx broadcast tx_signed.json
Você pode opcionalmente passar a flag --broadcast-mode
para especificar qual resposta receber do nó:
block
: o CLI aguarda que o tx seja confirmado em um bloco.sync
: o CLI aguarda apenas a resposta de execução do CheckTx.async
: o CLI retorna imediatamente (a transação pode falhar).
Codificando uma Transação
Para transmitir uma transação usando os endpoints gRPC ou REST, a transação precisará ser codificada primeiro. Isso pode ser feito usando o CLI.
Codificar uma transação é feito usando o seguinte comando:
crossfid tx encode tx_signed.json
Isso lerá a transação do arquivo, serializará usando Protobuf e exibirá os bytes da transação como base64 no console.
Decodificando uma Transação
O CLI também pode ser usado para decodificar bytes de transação.
Decodificar uma transação é feito usando o seguinte comando:
crossfid tx decode [protobuf-byte-string]
Isso decodificará os bytes da transação e exibirá a transação como JSON no console. Você também pode salvar a transação em um arquivo adicionando > tx.json
ao comando acima.
Programaticamente com Go
É possível manipular transações programaticamente através de Go usando a interface TxBuilder
do Cosmos SDK.
Gerando uma Transação
Antes de gerar uma transação, uma nova instância de um TxBuilder
precisa ser criada. Como o Cosmos SDK oferece suporte a transações Amino e Protobuf, o primeiro passo seria decidir qual esquema de codificação usar. Todas as etapas subsequentes permanecem inalteradas, independentemente de você estar usando Amino ou Protobuf, pois o TxBuilder
abstrai os mecanismos de codificação. No snippet seguinte, usaremos Protobuf.
import (
"github.com/cosmos/cosmos-sdk/simapp"
)
func sendTx() error
Podemos também configurar algumas chaves e endereços que enviarão e receberão as transações. Aqui, para fins de tutorial, usaremos alguns dados fictícios para criar chaves.
import (
"github.com/cosmos/cosmos-sdk/testutil/testdata"
)
priv1, _, addr1 := testdata.KeyTestPubAddr()
priv2, _, addr2 := testdata.KeyTestPubAddr()
priv3, _, addr3 := testdata.KeyTestPubAddr()
Preencher o TxBuilder
pode ser feito através de seus métodos:
import (
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
func sendTx() error
txBuilder.SetGasLimit(...)
txBuilder.SetFeeAmount(...)
txBuilder.SetMemo(...)
txBuilder.SetTimeoutHeight(...)
}
Neste ponto, a transação subjacente do TxBuilder
está pronta para ser assinada.
Assinando uma Transação
Configuramos a configuração de codificação para usar Protobuf, que usará SIGN_MODE_DIRECT
por padrão. Conforme ADR-020, cada signatário precisa assinar as SignerInfo
s de todos os outros signatários. Isso significa que precisamos realizar duas etapas sequencialmente:
- para cada signatário, preencher o
SignerInfo
do signatário dentro doTxBuilder
, - quando todas as
SignerInfo
s estiverem preenchidas, para cada signatário, assinar oSignDoc
(a carga a ser assinada).
Na atual API do TxBuilder
, ambas as etapas são feitas usando o mesmo método: SetSignatures()
. A API atual exige que primeiro façamos uma rodada de SetSignatures()
com assinaturas vazias, apenas para preencher SignerInfo
s, e uma segunda rodada de SetSignatures()
para realmente assinar a carga correta.
import (
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
)
func sendTx() error
accNums:= []uint64 // Os números de conta das contas
accSeqs:= []uint64 // Os números de sequência das contas
// Primeira rodada: reunimos todas as informações dos signatários. Usamos o "truque de configurar assinatura vazia" para fazer isso.
var sigsV2 []signing.SignatureV2
for i, priv := range privs ,
Sequence: accSeqs[i],
}
sigsV2 = append(sigsV2, sigV2)
}
err := txBuilder.SetSignatures(sigsV2...)
if err != nil
// Segunda rodada: todas as informações dos signatários estão configuradas, então cada signatário pode assinar.
sigsV2 = []signing.SignatureV2{}
for i, priv := range privs
sigV2, err := tx.SignWithPrivKey(
encCfg.TxConfig.SignModeHandler().DefaultMode(), signerData,
txBuilder, priv, encCfg.TxConfig, accSeqs[i])
if err != nil
sigsV2 = append(sigsV2, sigV2)
}
err = txBuilder.SetSignatures(sigsV2...)
if err != nil
}
O TxBuilder
agora está corretamente preenchido. Para imprimi-lo, você pode usar a interface TxConfig
da configuração inicial de codificação encCfg
:
func sendTx() error
// Gerar uma string JSON.
txJSONBytes, err := encCfg.TxConfig.TxJSONEncoder()(txBuilder.GetTx())
if err != nil
txJSON := string(txJSONBytes)
}
Usando gRPC
Não é possível gerar ou assinar uma transação usando gRPC, apenas transmiti-la. Para transmitir uma transação usando gRPC, você precisará gerar, assinar e codificar a transação usando o CLI ou programaticamente com Go.
Transmitindo uma Transação
A transmissão de uma transação pelo endpoint gRPC pode ser feita enviando uma requisição BroadcastTx
como a seguir, onde os txBytes
são os bytes codificados em protobuf de uma transação assinada:
grpcurl -plaintext \
-d '}","mode":"BROADCAST_MODE_SYNC"}' \
localhost:9090 \
cosmos.tx.v1beta1.Service/BroadcastTx
Usando REST
Não é possível gerar ou assinar uma transação usando REST, apenas transmiti-la. Para transmitir uma transação usando REST, você precisará gerar, assinar e codificar a transação usando o CLI ou programaticamente com Go.
Transmitindo uma Transação
A transmissão de uma transação pelo endpoint REST (servido por gRPC-gateway
) pode ser feita enviando uma requisição POST como a seguir, onde os txBytes
são os bytes codificados em protobuf de uma transação assinada:
curl -X POST \
-H "Content-Type: application/json" \
-d'}","mode":"BROADCAST_MODE_SYNC"}' \
localhost:1317/cosmos/tx/v1beta1/txs
Usando CosmJS (JavaScript & TypeScript)
CosmJS visa criar bibliotecas de clientes em JavaScript que podem ser incorporadas em aplicações web. Consulte https://cosmos.github.io/cosmjs para mais informações. Em janeiro de 2021, a documentação do CosmJS ainda estava em andamento.