github.com/Tri-stone/burrow@v0.25.0/execution/transactor.go (about)

     1  // Copyright 2017 Monax Industries Limited
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package execution
    16  
    17  import (
    18  	"context"
    19  	"encoding/json"
    20  	"fmt"
    21  	"sync"
    22  
    23  	"github.com/hyperledger/burrow/acm"
    24  	"github.com/hyperledger/burrow/bcm"
    25  	"github.com/hyperledger/burrow/consensus/tendermint/codes"
    26  	"github.com/hyperledger/burrow/crypto"
    27  	"github.com/hyperledger/burrow/event"
    28  	"github.com/hyperledger/burrow/execution/errors"
    29  	"github.com/hyperledger/burrow/execution/exec"
    30  	"github.com/hyperledger/burrow/logging"
    31  	"github.com/hyperledger/burrow/logging/structure"
    32  	"github.com/hyperledger/burrow/txs"
    33  	abciTypes "github.com/tendermint/tendermint/abci/types"
    34  	tmTypes "github.com/tendermint/tendermint/types"
    35  )
    36  
    37  const (
    38  	SubscribeBufferSize = 10
    39  )
    40  
    41  // Transactor is responsible for helping to formulate, sign, and broadcast transactions to tendermint
    42  //
    43  // The BroadcastTx* methods are able to work against the mempool Accounts (pending) state rather than the
    44  // committed (final) Accounts state and can assign a sequence number based on all of the txs
    45  // seen since the last block - provided these transactions are successfully committed (via DeliverTx) then
    46  // subsequent transactions will have valid sequence numbers. This allows Burrow to coordinate sequencing and signing
    47  // for a key it holds or is provided - it is down to the key-holder to manage the mutual information between transactions
    48  // concurrent within a new block window.
    49  type Transactor struct {
    50  	BlockchainInfo  bcm.BlockchainInfo
    51  	Emitter         *event.Emitter
    52  	MempoolAccounts *Accounts
    53  	checkTxAsync    func(tx tmTypes.Tx, cb func(*abciTypes.Response)) error
    54  	txEncoder       txs.Encoder
    55  	logger          *logging.Logger
    56  }
    57  
    58  func NewTransactor(tip bcm.BlockchainInfo, emitter *event.Emitter, mempoolAccounts *Accounts,
    59  	checkTxAsync func(tx tmTypes.Tx, cb func(*abciTypes.Response)) error, txEncoder txs.Encoder,
    60  	logger *logging.Logger) *Transactor {
    61  
    62  	return &Transactor{
    63  		BlockchainInfo:  tip,
    64  		Emitter:         emitter,
    65  		MempoolAccounts: mempoolAccounts,
    66  		checkTxAsync:    checkTxAsync,
    67  		txEncoder:       txEncoder,
    68  		logger:          logger.With(structure.ComponentKey, "Transactor"),
    69  	}
    70  }
    71  
    72  func (trans *Transactor) BroadcastTxSync(ctx context.Context, txEnv *txs.Envelope) (*exec.TxExecution, error) {
    73  	// Sign unless already signed - note we must attempt signing before subscribing so we get accurate final TxHash
    74  	unlock, err := trans.MaybeSignTxMempool(txEnv)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	// We will try and call this before the function exits unless we error but it is idempotent
    79  	defer unlock()
    80  	// Subscribe before submitting to mempool
    81  	txHash := txEnv.Tx.Hash()
    82  	subID := event.GenSubID()
    83  	out, err := trans.Emitter.Subscribe(ctx, subID, exec.QueryForTxExecution(txHash), SubscribeBufferSize)
    84  	if err != nil {
    85  		// We do not want to hold the lock with a defer so we must
    86  		return nil, err
    87  	}
    88  	defer trans.Emitter.UnsubscribeAll(context.Background(), subID)
    89  	// Push Tx to mempool
    90  	checkTxReceipt, err := trans.CheckTxSync(ctx, txEnv)
    91  	unlock()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	// Get all the execution events for this Tx
    96  	select {
    97  	case <-ctx.Done():
    98  		syncInfo := bcm.GetSyncInfo(trans.BlockchainInfo)
    99  		bs, err := json.Marshal(syncInfo)
   100  		syncInfoString := string(bs)
   101  		if err != nil {
   102  			syncInfoString = fmt.Sprintf("{error could not marshal SyncInfo: %v}", err)
   103  		}
   104  		return nil, fmt.Errorf("waiting for tx %v, SyncInfo: %s", checkTxReceipt.TxHash, syncInfoString)
   105  	case msg := <-out:
   106  		txe := msg.(*exec.TxExecution)
   107  		callError := txe.CallError()
   108  		if callError != nil && callError.ErrorCode() != errors.ErrorCodeExecutionReverted {
   109  			return nil, errors.Wrap(callError, "exception during transaction execution")
   110  		}
   111  		return txe, nil
   112  	}
   113  }
   114  
   115  // Broadcast a transaction without waiting for confirmation - will attempt to sign server-side and set sequence numbers
   116  // if no signatures are provided
   117  func (trans *Transactor) BroadcastTxAsync(ctx context.Context, txEnv *txs.Envelope) (*txs.Receipt, error) {
   118  	return trans.CheckTxSync(ctx, txEnv)
   119  }
   120  
   121  // Broadcast a transaction and waits for a response from the mempool. Transactions to BroadcastTx will block during
   122  // various mempool operations (managed by Tendermint) including mempool Reap, Commit, and recheckTx.
   123  func (trans *Transactor) CheckTxSync(ctx context.Context, txEnv *txs.Envelope) (*txs.Receipt, error) {
   124  	trans.logger.Trace.Log("method", "CheckTxSync",
   125  		structure.TxHashKey, txEnv.Tx.Hash(),
   126  		"tx", txEnv.String())
   127  	// Sign unless already signed
   128  	unlock, err := trans.MaybeSignTxMempool(txEnv)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	defer unlock()
   133  	err = txEnv.Validate()
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	txBytes, err := trans.txEncoder.EncodeTx(txEnv)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	return trans.CheckTxSyncRaw(ctx, txBytes)
   142  }
   143  
   144  func (trans *Transactor) MaybeSignTxMempool(txEnv *txs.Envelope) (UnlockFunc, error) {
   145  	// Sign unless already signed
   146  	if len(txEnv.Signatories) == 0 {
   147  		var err error
   148  		var unlock UnlockFunc
   149  		// We are writing signatures back to txEnv so don't shadow txEnv here
   150  		txEnv, unlock, err = trans.SignTxMempool(txEnv)
   151  		if err != nil {
   152  			return nil, fmt.Errorf("error signing transaction: %v", err)
   153  		}
   154  		// Hash will have change since we signed
   155  		txEnv.Tx.Rehash()
   156  		// Make this idempotent for defer
   157  		var once sync.Once
   158  		return func() { once.Do(unlock) }, nil
   159  	}
   160  	return func() {}, nil
   161  }
   162  
   163  func (trans *Transactor) SignTxMempool(txEnv *txs.Envelope) (*txs.Envelope, UnlockFunc, error) {
   164  	inputs := txEnv.Tx.GetInputs()
   165  	signers := make([]acm.AddressableSigner, len(inputs))
   166  	unlockers := make([]UnlockFunc, len(inputs))
   167  	for i, input := range inputs {
   168  		ssa, err := trans.MempoolAccounts.SequentialSigningAccount(input.Address)
   169  		if err != nil {
   170  			return nil, nil, err
   171  		}
   172  		sa, unlock, err := ssa.Lock()
   173  		if err != nil {
   174  			return nil, nil, err
   175  		}
   176  		// Hold lock until safely in mempool - important that this is held until after CheckTxSync returns
   177  		unlockers[i] = unlock
   178  		signers[i] = sa
   179  		// Set sequence number consecutively from mempool
   180  		input.Sequence = sa.Sequence + 1
   181  	}
   182  
   183  	err := txEnv.Sign(signers...)
   184  	if err != nil {
   185  		return nil, nil, err
   186  	}
   187  	return txEnv, UnlockFunc(func() {
   188  		for _, unlock := range unlockers {
   189  			defer unlock()
   190  		}
   191  	}), nil
   192  }
   193  
   194  func (trans *Transactor) SignTx(txEnv *txs.Envelope) (*txs.Envelope, error) {
   195  	var err error
   196  	inputs := txEnv.Tx.GetInputs()
   197  	signers := make([]acm.AddressableSigner, len(inputs))
   198  	for i, input := range inputs {
   199  		signers[i], err = trans.MempoolAccounts.SigningAccount(input.Address)
   200  	}
   201  	err = txEnv.Sign(signers...)
   202  	if err != nil {
   203  		return nil, err
   204  	}
   205  	return txEnv, nil
   206  }
   207  
   208  func (trans *Transactor) CheckTxSyncRaw(ctx context.Context, txBytes []byte) (*txs.Receipt, error) {
   209  	responseCh := make(chan *abciTypes.Response, 3)
   210  	err := trans.CheckTxAsyncRaw(txBytes, func(res *abciTypes.Response) {
   211  		responseCh <- res
   212  	})
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	select {
   218  	case <-ctx.Done():
   219  		return nil, fmt.Errorf("waiting for CheckTx response in CheckTxSyncRaw: %v", ctx.Err())
   220  	case response := <-responseCh:
   221  		checkTxResponse := response.GetCheckTx()
   222  		if checkTxResponse == nil {
   223  			return nil, fmt.Errorf("application did not return CheckTx response")
   224  		}
   225  
   226  		switch checkTxResponse.Code {
   227  		case codes.TxExecutionSuccessCode:
   228  			receipt, err := txs.DecodeReceipt(checkTxResponse.Data)
   229  			if err != nil {
   230  				return nil, fmt.Errorf("could not deserialise transaction receipt: %s", err)
   231  			}
   232  			return receipt, nil
   233  		default:
   234  			return nil, errors.ErrorCodef(errors.Code(checkTxResponse.Code),
   235  				"error returned by Tendermint in BroadcastTxSync ABCI log: %v", checkTxResponse.Log)
   236  		}
   237  	}
   238  }
   239  
   240  func (trans *Transactor) CheckTxAsyncRaw(txBytes []byte, callback func(res *abciTypes.Response)) error {
   241  	return trans.checkTxAsync(txBytes, callback)
   242  }
   243  
   244  func (trans *Transactor) CheckTxAsync(txEnv *txs.Envelope, callback func(res *abciTypes.Response)) error {
   245  	err := txEnv.Validate()
   246  	if err != nil {
   247  		return err
   248  	}
   249  	txBytes, err := trans.txEncoder.EncodeTx(txEnv)
   250  	if err != nil {
   251  		return fmt.Errorf("error encoding transaction: %v", err)
   252  	}
   253  	return trans.CheckTxAsyncRaw(txBytes, callback)
   254  }
   255  
   256  func (trans *Transactor) CallCodeSim(fromAddress crypto.Address, code, data []byte) (*exec.TxExecution, error) {
   257  	return CallCodeSim(trans.MempoolAccounts, trans.BlockchainInfo, fromAddress, fromAddress, code, data, trans.logger)
   258  }
   259  
   260  func (trans *Transactor) CallSim(fromAddress, address crypto.Address, data []byte) (*exec.TxExecution, error) {
   261  	return CallSim(trans.MempoolAccounts, trans.BlockchainInfo, fromAddress, address, data, trans.logger)
   262  }