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