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 }