burrow tx commit
RPC -> Transactor.BroadcastTxSync -> Mempoo.CheckTx -> abci.CheckTx -> executor.Execute -> SendContext.Execute
Call stack:
github.com/hyperledger/burrow/execution/contexts.(*SendContext).Execute at send_context.go:20
github.com/hyperledger/burrow/execution.(*executor).Execute at execution.go:254
github.com/hyperledger/burrow/consensus/abci.ExecuteTx at execute_tx.go:30
github.com/hyperledger/burrow/consensus/abci.(*App).CheckTx at app.go:189
github.com/tendermint/tendermint/abci/client.(*localClient).CheckTxAsync at local_client.go:99
github.com/tendermint/tendermint/proxy.(*appConnMempool).CheckTxAsync at app_conn.go:114
github.com/tendermint/tendermint/mempool.(*CListMempool).CheckTx at clist_mempool.go:281
github.com/tendermint/tendermint/mempool.Mempool.CheckTx-fm at mempool.go:18
github.com/hyperledger/burrow/execution.(*Transactor).CheckTxAsyncRaw at transactor.go:237
github.com/hyperledger/burrow/execution.(*Transactor).CheckTxSyncRaw at transactor.go:206
github.com/hyperledger/burrow/execution.(*Transactor).CheckTxSync at transactor.go:134
github.com/hyperledger/burrow/execution.(*Transactor).BroadcastTxSync at transactor.go:83
github.com/hyperledger/burrow/rpc/rpctransact.(*transactServer).BroadcastTxSync at transact_server.go:55
github.com/hyperledger/burrow/rpc/rpctransact._Transact_BroadcastTxSync_Handler.func1 at rpctransact.pb.go:505
github.com/hyperledger/burrow/rpc.unaryInterceptor.func1 at grpc.go:31
github.com/hyperledger/burrow/rpc/rpctransact._Transact_BroadcastTxSync_Handler at rpctransact.pb.go:507
google.golang.org/grpc.(*Server).processUnaryRPC at server.go:1024
google.golang.org/grpc.(*Server).handleStream at server.go:1313
google.golang.org/grpc.(*Server).serveStreams.func1.1 at server.go:722
runtime.goexit at asm_amd64.s:1357
- Async stack trace
google.golang.org/grpc.(*Server).serveStreams.func1 at server.go:720
TX executor
Verify and execute the ‘TypeSend’ context: exe.contexts[txEnv.Tx.Type()]
// If the tx is invalid, an error will be returned.
// Unlike ExecBlock(), state will not be altered.
func (exe *executor) Execute(txEnv *txs.Envelope) (txe *exec.TxExecution, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v\n%s", txEnv.String(), r,
debug.Stack())
}
}()
logger := exe.logger.WithScope("executor.Execute(tx txs.Tx)").With(
"height", exe.block.Height,
"run_call", exe.runCall,
structure.TxHashKey, txEnv.Tx.Hash())
logger.InfoMsg("Executing transaction", "tx", txEnv.String())
// Verify transaction signature against inputs
err = txEnv.Verify(exe.params.ChainID)
if err != nil {
logger.InfoMsg("Transaction Verify failed", structure.ErrorKey, err)
return nil, err
}
if txExecutor, ok := exe.contexts[txEnv.Tx.Type()]; ok {
// Establish new TxExecution
txe := exe.block.Tx(txEnv)
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from panic in executor.Execute(%s): %v\n%s", txEnv.String(), r,
debug.Stack())
}
}()
err = exe.validateInputsAndStorePublicKeys(txEnv)
if err != nil {
logger.InfoMsg("Transaction validate failed", structure.ErrorKey, err)
txe.PushError(err)
return nil, err
}
err = txExecutor.Execute(txe, txe.Envelope.Tx.Payload)
if err != nil {
logger.InfoMsg("Transaction execution failed", structure.ErrorKey, err)
txe.PushError(err)
return nil, err
}
// Increment sequence numbers for Tx inputs
err = exe.updateSequenceNumbers(txEnv)
if err != nil {
logger.InfoMsg("Updating sequences failed", structure.ErrorKey, err)
txe.PushError(err)
return nil, err
}
// Return execution for this tx
return txe, nil
}
return nil, fmt.Errorf("unknown transaction type: %v", txEnv.Tx.Type())
}
Executor context
0 = TypeUnbond (18) ->
1 = TypeBond (17) ->
2 = TypeProposal (35) ->
3 = TypeIdentify (36) ->
4 = TypeCall (2) ->
5 = TypeSend (1) ->
6 = TypeName (3) ->
7 = TypePermissions (33) ->
8 = TypeGovernance (34) ->
TypeSend
- Check permission
- Make output account if need
- Check account balance
- adjust by input and output
func (ctx *SendContext) Execute(txe *exec.TxExecution, p payload.Payload) error {
var ok bool
ctx.tx, ok = p.(*payload.SendTx)
if !ok {
return fmt.Errorf("payload must be SendTx, but is: %v", txe.Envelope.Tx.Payload)
}
accounts, inTotal, err := getInputs(ctx.State, ctx.tx.Inputs)
if err != nil {
return err
}
// ensure all inputs have send permissions
err = allHavePermission(ctx.State, permission.Send, accounts, ctx.Logger)
if err != nil {
return errors.Wrap(err, "at least one input lacks permission for SendTx")
}
// add outputs to accounts map
// if any outputs don't exist, all inputs must have CreateAccount perm
accounts, err = getOrMakeOutputs(ctx.State, accounts, ctx.tx.Outputs, ctx.Logger)
if err != nil {
return err
}
outTotal, err := validateOutputs(ctx.tx.Outputs)
if err != nil {
return err
}
if outTotal > inTotal {
return errors.Codes.InsufficientFunds
}
if outTotal < inTotal {
return errors.Codes.Overpayment
}
if outTotal == 0 {
return errors.Codes.ZeroPayment
}
// Good! Adjust accounts
err = adjustByInputs(accounts, ctx.tx.Inputs)
if err != nil {
return err
}
err = adjustByOutputs(accounts, ctx.tx.Outputs)
if err != nil {
return err
}
for _, acc := range accounts {
err = ctx.State.UpdateAccount(acc)
if err != nil {
return err
}
}
for _, i := range ctx.tx.Inputs {
txe.Input(i.Address, nil)
}
for _, o := range ctx.tx.Outputs {
txe.Output(o.Address, nil)
}
return nil
}
allHavePermission
It wll check the accounts of Input for permissions:
func allHavePermission(accountGetter acmstate.AccountGetter, perm permission.PermFlag,
accs map[crypto.Address]*acm.Account, logger *logging.Logger) error {
for _, acc := range accs {
if !HasPermission(accountGetter, acc, perm, logger) {
return errors.PermissionDenied{
Address: acc.Address,
Perm: perm,
}
}
}
return nil
}