github.com/datachainlab/burrow@v0.25.0/execution/exec/stream_event.go (about)

     1  package exec
     2  
     3  import (
     4  	"io"
     5  )
     6  
     7  type EventStream interface {
     8  	Recv() (*StreamEvent, error)
     9  }
    10  
    11  type StreamEvents []*StreamEvent
    12  
    13  func (ses *StreamEvents) Recv() (*StreamEvent, error) {
    14  	evs := *ses
    15  	if len(evs) == 0 {
    16  		return nil, io.EOF
    17  	}
    18  	ev := evs[0]
    19  	*ses = evs[1:]
    20  	return ev, nil
    21  }
    22  
    23  func ConsumeBlockExecution(stream EventStream) (block *BlockExecution, err error) {
    24  	var ev *StreamEvent
    25  	accum := new(BlockAccumulator)
    26  	for ev, err = stream.Recv(); err == nil; ev, err = stream.Recv() {
    27  		block = accum.Consume(ev)
    28  		if block != nil {
    29  			return block, nil
    30  		}
    31  	}
    32  	// If we reach here then we have failed to consume a complete block
    33  	return nil, err
    34  }
    35  
    36  type BlockAccumulator struct {
    37  	block *BlockExecution
    38  	stack TxStack
    39  }
    40  
    41  // Consume will add the StreamEvent passed to the block accumulator and if the block complete is complete return the
    42  // BlockExecution, otherwise will return nil
    43  func (ba *BlockAccumulator) Consume(ev *StreamEvent) *BlockExecution {
    44  	switch {
    45  	case ev.BeginBlock != nil:
    46  		ba.block = &BlockExecution{
    47  			Height: ev.BeginBlock.Height,
    48  			Header: ev.BeginBlock.Header,
    49  		}
    50  	case ev.BeginTx != nil, ev.Envelope != nil, ev.Event != nil, ev.EndTx != nil:
    51  		txe := ba.stack.Consume(ev)
    52  		if txe != nil {
    53  			ba.block.TxExecutions = append(ba.block.TxExecutions, txe)
    54  		}
    55  	case ev.EndBlock != nil:
    56  		return ba.block
    57  	}
    58  	return nil
    59  }
    60  
    61  // TxStack is able to consume potentially nested txs
    62  type TxStack []*TxExecution
    63  
    64  func (stack TxStack) Peek() *TxExecution {
    65  	return stack[len(stack)-1]
    66  }
    67  
    68  func (stack *TxStack) Push(txe *TxExecution) {
    69  	// Put this txe in the parent position
    70  	*stack = append(*stack, txe)
    71  }
    72  
    73  func (stack *TxStack) Pop() *TxExecution {
    74  	s := *stack
    75  	txc := s.Peek()
    76  	*stack = s[:len(s)-1]
    77  	return txc
    78  }
    79  
    80  // Consume will add the StreamEvent to the transaction stack and if that completes a single outermost transaction
    81  // returns the TxExecution otherwise will return nil
    82  func (stack *TxStack) Consume(ev *StreamEvent) *TxExecution {
    83  	switch {
    84  	case ev.BeginTx != nil:
    85  		stack.Push(initTx(ev.BeginTx))
    86  	case ev.Envelope != nil:
    87  		txe := stack.Peek()
    88  		txe.Envelope = ev.Envelope
    89  		txe.Receipt = txe.Envelope.Tx.GenerateReceipt()
    90  	case ev.Event != nil:
    91  		txe := stack.Peek()
    92  		txe.Events = append(txe.Events, ev.Event)
    93  	case ev.EndTx != nil:
    94  		txe := stack.Pop()
    95  		if len(*stack) == 0 {
    96  			// This terminates the outermost transaction
    97  			return txe
    98  		}
    99  		// If there is a parent tx on the stack add this tx as child
   100  		parent := stack.Peek()
   101  		parent.TxExecutions = append(parent.TxExecutions, txe)
   102  	}
   103  	return nil
   104  }
   105  
   106  func initTx(beginTx *BeginTx) *TxExecution {
   107  	return &TxExecution{
   108  		TxHeader:  beginTx.TxHeader,
   109  		Result:    beginTx.Result,
   110  		Exception: beginTx.Exception,
   111  	}
   112  }