github.com/cranelv/ethereum_mpc@v0.0.0-20191031014521-23aeb1415092/consensus_pbft/executor/executor.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package executor 18 19 import ( 20 "github.com/ethereum/go-ethereum/consensus_pbft" 21 "github.com/ethereum/go-ethereum/consensus_pbft/util/events" 22 23 "github.com/ethereum/go-ethereum/consensus_pbft/consensusInterface" 24 "github.com/ethereum/go-ethereum/consensus_pbft/message" 25 "github.com/ethereum/go-ethereum/consensus_pbft/pbftTypes" 26 "github.com/ethereum/go-ethereum/consensus_pbft/singletons" 27 ) 28 29 // PartialStack contains the ledger features required by the executor.Coordinator 30 type PartialStack interface { 31 consensusInterface.NodeExecutor 32 GetStateInfo() *message.StateInfo 33 } 34 35 type coordinatorImpl struct { 36 manager events.Manager // Maintains event thread and sends events to the coordinator 37 rawExecutor PartialStack // Does the real interaction with the ledger 38 consumer consensus_pbft.ExecutionConsumer // The consumer of this coordinator which receives the callbacks 39 stc consensusInterface.Coordinator // State transfer instance 40 batchInProgress bool // Are we mid execution batch 41 skipInProgress bool // Are we mid state transfer 42 } 43 44 // NewCoordinatorImpl creates a new executor.Coordinator 45 func NewImpl(consumer consensus_pbft.ExecutionConsumer, rawExecutor PartialStack) consensus_pbft.Executor { 46 co := &coordinatorImpl{ 47 rawExecutor: rawExecutor, 48 consumer: consumer, 49 // stc: statetransfer.NewCoordinatorImpl(stps), 50 manager: events.NewManagerImpl(), 51 } 52 co.manager.SetReceiver(co) 53 return co 54 } 55 56 // ProcessEvent is the main event loop for the executor.Coordinator 57 func (co *coordinatorImpl) ProcessEvent(event events.Event) events.Event { 58 switch et := event.(type) { 59 case executeEvent: 60 singletons.Log.Debug("Executor is processing an executeEvent") 61 if co.skipInProgress { 62 singletons.Log.Error("FATAL programming error, attempted to execute a transaction during state transfer") 63 return nil 64 } 65 66 if !co.batchInProgress { 67 singletons.Log.Debug("Starting new transaction batch") 68 co.batchInProgress = true 69 err := co.rawExecutor.BeginTaskBatch(co) 70 _ = err // TODO This should probably panic, see issue 752 71 } 72 73 co.rawExecutor.ExecTasks(co, et.tasks) 74 75 co.consumer.Executed(et.tag) 76 case commitEvent: 77 singletons.Log.Debug("Executor is processing an commitEvent") 78 if co.skipInProgress { 79 singletons.Log.Error("Likely FATAL programming error, attempted to commit a transaction batch during state transfer") 80 return nil 81 } 82 83 if !co.batchInProgress { 84 singletons.Log.Error("Likely FATAL programming error, attemted to commit a transaction batch when one does not exist") 85 return nil 86 } 87 88 _, err := co.rawExecutor.CommitTaskBatch(co, et.metadata) 89 _ = err // TODO This should probably panic, see issue 752 90 91 co.batchInProgress = false 92 93 info := co.rawExecutor.GetStateInfo() 94 95 singletons.Log.Debugf("Committed tasksInfo with hash %x to chain", info.Digest()) 96 97 co.consumer.Committed(et.tag, info) 98 case rollbackEvent: 99 singletons.Log.Debug("Executor is processing an rollbackEvent") 100 if co.skipInProgress { 101 singletons.Log.Error("Programming error, attempted to rollback a transaction batch during state transfer") 102 return nil 103 } 104 105 if !co.batchInProgress { 106 singletons.Log.Error("Programming error, attempted to rollback a transaction batch which had not started") 107 return nil 108 } 109 110 err := co.rawExecutor.RollbackTaskBatch(co) 111 _ = err // TODO This should probably panic, see issue 752 112 113 co.batchInProgress = false 114 115 co.consumer.RolledBack(et.tag) 116 case stateUpdateEvent: 117 singletons.Log.Debug("Executor is processing a stateUpdateEvent") 118 if co.batchInProgress { 119 err := co.rawExecutor.RollbackTaskBatch(co) 120 _ = err // TODO This should probably panic, see issue 752 121 } 122 123 co.skipInProgress = true 124 125 info := et.stateInfo 126 for { 127 err, recoverable := co.stc.SyncToTarget( info.Height(), info.Digest(), et.peers) 128 if err == nil { 129 singletons.Log.Debug("State transfer sync completed, returning") 130 co.skipInProgress = false 131 co.consumer.StateUpdated(et.tag, info) 132 return nil 133 } 134 if !recoverable { 135 singletons.Log.Warnf("State transfer failed irrecoverably, calling back to consumer: %s", err) 136 co.consumer.StateUpdated(et.tag, nil) 137 return nil 138 } 139 singletons.Log.Warnf("State transfer did not complete successfully but is recoverable, trying again: %s", err) 140 et.peers = nil // Broaden the peers included in recover to all connected 141 } 142 default: 143 singletons.Log.Error("Unknown event type %s", et) 144 } 145 146 return nil 147 } 148 149 // Commit commits whatever outstanding requests have been executed, it is an error to call this without pending executions 150 func (co *coordinatorImpl) Commit(tag interface{}, metadata []byte) { 151 co.manager.Queue() <- commitEvent{tag, metadata} 152 } 153 154 // Execute adds additional executions to the current batch 155 func (co *coordinatorImpl) Execute(tag interface{}, txs []*message.Task) { 156 co.manager.Queue() <- executeEvent{tag, txs} 157 } 158 159 // Rollback rolls back the executions from the current batch 160 func (co *coordinatorImpl) Rollback(tag interface{}) { 161 co.manager.Queue() <- rollbackEvent{tag} 162 } 163 164 // UpdateState uses the state transfer subsystem to attempt to progress to a target 165 func (co *coordinatorImpl) UpdateState(tag interface{}, info *message.StateInfo, peers []*pbftTypes.PeerID) { 166 co.manager.Queue() <- stateUpdateEvent{tag, info, peers} 167 } 168 169 // Start must be called before utilizing the Coordinator 170 func (co *coordinatorImpl) Start() { 171 co.stc.Start() 172 co.manager.Start() 173 } 174 175 // Halt should be called to clean up resources allocated by the Coordinator 176 func (co *coordinatorImpl) Halt() { 177 co.stc.Stop() 178 co.manager.Halt() 179 } 180 181 // Event types 182 183 type executeEvent struct { 184 tag interface{} 185 tasks []*message.Task 186 } 187 188 // Note, this cannot be a simple type alias, in case tag is nil 189 type rollbackEvent struct { 190 tag interface{} 191 } 192 193 type commitEvent struct { 194 tag interface{} 195 metadata []byte 196 } 197 198 type stateUpdateEvent struct { 199 tag interface{} 200 stateInfo *message.StateInfo 201 peers []*pbftTypes.PeerID 202 }