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 }