github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/consensus/abci/process.go (about) 1 package abci 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "fmt" 7 "sync" 8 "time" 9 10 "github.com/hyperledger/burrow/bcm" 11 12 "github.com/hyperledger/burrow/execution" 13 "github.com/hyperledger/burrow/txs" 14 "github.com/tendermint/tendermint/abci/types" 15 "github.com/tendermint/tendermint/mempool" 16 tmTypes "github.com/tendermint/tendermint/types" 17 ) 18 19 type Process struct { 20 ticker *time.Ticker 21 committer execution.BatchCommitter 22 blockchain *bcm.Blockchain 23 done chan struct{} 24 panic func(error) 25 commitNeeded bool 26 txDecoder txs.Decoder 27 shutdownOnce sync.Once 28 } 29 30 // NewProcess returns a no-consensus ABCI process suitable for running a single node without Tendermint. 31 // The CheckTx function can be used to submit transactions which are processed according 32 func NewProcess(committer execution.BatchCommitter, blockchain *bcm.Blockchain, txDecoder txs.Decoder, 33 commitInterval time.Duration, panicFunc func(error)) *Process { 34 35 p := &Process{ 36 committer: committer, 37 blockchain: blockchain, 38 done: make(chan struct{}), 39 txDecoder: txDecoder, 40 panic: panicFunc, 41 } 42 43 if commitInterval != 0 { 44 p.ticker = time.NewTicker(commitInterval) 45 go p.triggerCommits() 46 } 47 48 return p 49 } 50 51 func (p *Process) CheckTx(tx tmTypes.Tx, cb func(*types.Response), txInfo mempool.TxInfo) error { 52 const header = "DeliverTx" 53 p.committer.Lock() 54 defer p.committer.Unlock() 55 // Skip check - deliver immediately 56 // FIXME: [Silas] this means that any transaction that a transaction that fails CheckTx 57 // that would not normally end up stored in state (as an exceptional tx) will get stored in state. 58 // This means that the same sequence of transactions fed to no consensus mode can give rise to a state with additional 59 // invalid transactions in state. Since the state hash is non-deterministic based on when the commits happen it's not 60 // clear this is a problem. The underlying state will be compatible. 61 checkTx := ExecuteTx(header, p.committer, p.txDecoder, tx) 62 cb(types.ToResponseCheckTx(checkTx)) 63 p.commitNeeded = true 64 if p.ticker == nil { 65 err := p.commit() 66 if err != nil { 67 return err 68 } 69 } 70 return nil 71 } 72 73 func (p *Process) Shutdown(ctx context.Context) (err error) { 74 p.committer.Lock() 75 defer p.committer.Unlock() 76 p.shutdownOnce.Do(func() { 77 if p.ticker != nil { 78 p.ticker.Stop() 79 } 80 close(p.done) 81 }) 82 return 83 } 84 85 func (p *Process) triggerCommits() { 86 for { 87 select { 88 case <-p.ticker.C: 89 p.commitOrPanic() 90 case <-p.done: 91 // Escape loop since ticket channel is never closed 92 } 93 } 94 } 95 96 func (p *Process) commitOrPanic() { 97 p.committer.Lock() 98 defer p.committer.Unlock() 99 err := p.commit() 100 if err != nil { 101 p.panic(err) 102 } 103 } 104 105 func (p *Process) commit() error { 106 const errHeader = "commit():" 107 if !p.commitNeeded { 108 return nil 109 } 110 111 appHash, err := p.committer.Commit(nil) 112 if err != nil { 113 return fmt.Errorf("%s could not Commit tx %v", errHeader, err) 114 } 115 116 // Maintain a basic hashed linked list, mixing in the appHash as we go 117 hasher := sha256.New() 118 hasher.Write(appHash) 119 hasher.Write(p.blockchain.LastBlockHash()) 120 121 err = p.blockchain.CommitBlock(time.Now(), hasher.Sum(nil), appHash) 122 if err != nil { 123 return fmt.Errorf("%s could not CommitBlock %v", errHeader, err) 124 } 125 p.commitNeeded = false 126 return nil 127 }