ApplyTransactions() 1. Batch validate signatures for every transaction provided 2. Processes all transactions provided against the state machine 3. Allows ephemeral 'oversize' transaction processing without applying 'oversize txn' changes to the state 4. Returns the following for successful transact
(ctx context.Context, txs [][]byte, r *lib.ApplyBlockResults, allowOversize bool)
| 202 | // 4. Returns the following for successful transactions within a block: <results, tx-list, root, count> |
| 203 | // 5. Returns all transactions that failed during processing |
| 204 | func (s *StateMachine) ApplyTransactions(ctx context.Context, txs [][]byte, r *lib.ApplyBlockResults, allowOversize bool) lib.ErrorI { |
| 205 | // use a map to check for 'same-block' duplicate transactions |
| 206 | deDuplicator := lib.NewDeDuplicator[string]() |
| 207 | // use a batch verifier for signatures |
| 208 | batchVerifier := crypto.NewBatchVerifier() |
| 209 | // get the governance parameter for max block size |
| 210 | maxBlockSize, err := s.GetMaxBlockSize() |
| 211 | if err != nil { |
| 212 | return err |
| 213 | } |
| 214 | // keep a map to track transactions that failed 'check' |
| 215 | failedCheckTxs := map[int]error{} |
| 216 | // first batch validate signatures over the entire set |
| 217 | for i, tx := range txs { |
| 218 | if _, checkErr := s.CheckTx(tx, "", batchVerifier); checkErr != nil { |
| 219 | failedCheckTxs[i] = checkErr |
| 220 | } |
| 221 | } |
| 222 | // execute batch verification of the signatures in the block |
| 223 | for _, failedIdx := range batchVerifier.Verify() { |
| 224 | failedCheckTxs[failedIdx] = ErrInvalidSignature() |
| 225 | } |
| 226 | // set the store back to the original at the end of processing |
| 227 | originalStore := s.Store().(lib.StoreI) |
| 228 | defer s.SetStore(originalStore) |
| 229 | // create a variable to track if the block is over size |
| 230 | var oversize bool |
| 231 | // iterates over each transaction in the block |
| 232 | for i, tx := range txs { |
| 233 | // if interrupt signal |
| 234 | if ctx.Err() != nil { |
| 235 | return lib.ErrMempoolStopSignal() |
| 236 | } |
| 237 | // if already failed check tx or signature |
| 238 | if e, found := failedCheckTxs[i]; found { |
| 239 | r.AddFailed(lib.NewFailedTx(tx, e)) |
| 240 | continue |
| 241 | } |
| 242 | // calculate the hash of the transaction and convert it to a hex string |
| 243 | hashString := crypto.HashString(tx) |
| 244 | // check if the transaction is a 'same block' duplicate |
| 245 | if found := deDuplicator.Found(hashString); found { |
| 246 | return lib.ErrDuplicateTx(hashString) |
| 247 | } |
| 248 | // get the tx size |
| 249 | txSize := uint64(len(tx)) |
| 250 | // if the max block size is exceeded and we're not yet marked as 'oversize' |
| 251 | if txSize+r.BlockSize > maxBlockSize && !oversize { |
| 252 | // if validating a block - oversize shouldn't happen |
| 253 | if !allowOversize { |
| 254 | return ErrMaxBlockSize() |
| 255 | } |
| 256 | // set oversize to 'true' |
| 257 | oversize = true |
| 258 | // wrap the store in a 'database transaction' to rollback all the 'oversize transactions' |
| 259 | if _, e := s.TxnWrap(); e != nil { |
| 260 | return e |
| 261 | } |