github.com/cranelv/ethereum_mpc@v0.0.0-20191031014521-23aeb1415092/consensus_pbft/pbft/mock_ledger_test.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 pbft 18 19 import ( 20 "fmt" 21 "reflect" 22 "sync" 23 "time" 24 "github.com/ethereum/go-ethereum/consensus_pbft/pbftTypes" 25 "github.com/ethereum/go-ethereum/consensus_pbft" 26 "github.com/ethereum/go-ethereum/consensus_pbft/message" 27 "github.com/ethereum/go-ethereum/consensus_pbft/singletons" 28 ) 29 30 type LedgerDirectory interface { 31 GetLedgerByPeerID(peerID *pbftTypes.PeerID) (consensus_pbft.ReadOnlyLedger, bool) 32 } 33 34 type HashLedgerDirectory struct { 35 remoteLedgers map[pbftTypes.PeerID]consensus_pbft.ReadOnlyLedger 36 } 37 38 func (hd *HashLedgerDirectory) GetLedgerByPeerID(peerID *pbftTypes.PeerID) (consensus_pbft.ReadOnlyLedger, bool) { 39 ledger, ok := hd.remoteLedgers[*peerID] 40 return ledger, ok 41 } 42 43 func (hd *HashLedgerDirectory) GetPeers() ([]pbftTypes.Peer, error) { 44 _, network, err := hd.GetNetworkNodes() 45 return network , err 46 } 47 48 func (hd *HashLedgerDirectory) GetPeerNode() (pbftTypes.Peer, error) { 49 self, _, err := hd.GetNetworkNodes() 50 return self, err 51 } 52 type testPeer struct { 53 ID pbftTypes.PeerID 54 Type pbftTypes.Peer_Type 55 } 56 func(pp* testPeer)GetPeerId() *pbftTypes.PeerID{ 57 peerID := pbftTypes.PeerID(pp.ID) 58 return &peerID 59 } 60 func(pp* testPeer)GetType() pbftTypes.Peer_Type{ 61 return pp.Type 62 } 63 64 func (hd *HashLedgerDirectory) GetNetworkNodes() (self pbftTypes.Peer, network []pbftTypes.Peer, err error) { 65 network = make([]pbftTypes.Peer, len(hd.remoteLedgers)+1) 66 i := 0 67 for peerID := range hd.remoteLedgers { 68 network[i] = &testPeer{ 69 ID:peerID, 70 Type: pbftTypes.Peer_VALIDATOR, 71 } 72 i++ 73 } 74 peerId := stringToPeerId("self") 75 network[i] = &testPeer{ 76 ID: peerId, 77 Type: pbftTypes.Peer_VALIDATOR, 78 } 79 80 self = network[i] 81 return 82 } 83 84 func (hd *HashLedgerDirectory) GetNetworkNodeIDs() (self *pbftTypes.PeerID, network []*pbftTypes.PeerID, err error) { 85 oSelf, oNetwork, err := hd.GetNetworkNodes() 86 if nil != err { 87 return 88 } 89 90 self = oSelf.GetPeerId() 91 network = make([]*pbftTypes.PeerID, len(oNetwork)) 92 for i, endpoint := range oNetwork { 93 network[i] = endpoint.GetPeerId() 94 } 95 return 96 } 97 98 type MockLedger struct { 99 cleanML *MockLedger 100 blocks map[uint64]*message.StateInfo 101 blockHeight uint64 102 remoteLedgers LedgerDirectory 103 104 mutex *sync.Mutex 105 106 txID interface{} 107 curBatch []*message.Task 108 curResults []*message.Result 109 preBatchState uint64 110 111 ce *consumerEndpoint // To support the ExecTx stuff 112 } 113 114 func NewMockLedger(remoteLedgers LedgerDirectory) *MockLedger { 115 mock := &MockLedger{} 116 mock.mutex = &sync.Mutex{} 117 mock.blocks = make(map[uint64]*message.StateInfo) 118 mock.blockHeight = 1 119 mock.blocks[0] = &message.StateInfo{Hash:"0x10021",Number:0} 120 mock.remoteLedgers = remoteLedgers 121 122 return mock 123 } 124 125 func (mock *MockLedger) BeginTaskBatch(id interface{}) error { 126 if mock.txID != nil { 127 return fmt.Errorf("Tx batch is already active") 128 } 129 mock.txID = id 130 mock.curBatch = nil 131 mock.curResults = nil 132 return nil 133 } 134 135 func (mock *MockLedger) Execute(tag interface{}, txs []*message.Task) { 136 go func() { 137 if mock.txID == nil { 138 mock.BeginTaskBatch(mock) 139 } 140 141 _, err := mock.ExecTasks(mock, txs) 142 if err != nil { 143 panic(err) 144 } 145 mock.ce.consumer.Executed(tag) 146 }() 147 } 148 149 func (mock *MockLedger) Commit(tag interface{}, meta []byte) { 150 go func() { 151 _, err := mock.CommitTaskBatch(mock, meta) 152 if err != nil { 153 panic(err) 154 } 155 mock.ce.consumer.Committed(tag, mock.GetStateInfo()) 156 }() 157 } 158 159 func (mock *MockLedger) Rollback(tag interface{}) { 160 go func() { 161 mock.RollbackTaskBatch(mock) 162 mock.ce.consumer.RolledBack(tag) 163 }() 164 } 165 166 func (mock *MockLedger) ExecTasks(id interface{}, txs []*message.Task) ([]*message.Result, error) { 167 if !reflect.DeepEqual(mock.txID, id) { 168 return nil, fmt.Errorf("Invalid batch ID") 169 } 170 171 mock.curBatch = append(mock.curBatch, txs...) 172 var err error 173 var txResult []*message.Result 174 if nil != mock.ce && nil != mock.ce.execTasksResult { 175 txResult, err = mock.ce.execTasksResult(txs) 176 } else { 177 // This is basically a default fake default transaction execution 178 if nil == txs { 179 txs = []*message.Task{{Payload: []byte("DUMMY")}} 180 } 181 182 for _, task := range txs { 183 if task.Payload == nil { 184 task.Payload = []byte("DUMMY") 185 } 186 187 txResult = append(txResult, &message.Result{task.Type,task.TimeStamp,task.Payload}) 188 } 189 190 } 191 192 mock.curResults = append(mock.curResults, txResult...) 193 194 return txResult, err 195 } 196 197 func (mock *MockLedger) CommitTaskBatch(id interface{}, metadata []byte) (*message.StateInfo, error) { 198 block, err := mock.commonCommitTx(id, metadata, false) 199 if nil == err { 200 mock.txID = nil 201 mock.curBatch = nil 202 mock.curResults = nil 203 } 204 return &message.StateInfo{Payload:block}, err 205 } 206 207 func (mock *MockLedger) commonCommitTx(id interface{}, metadata []byte, preview bool) (*message.StateInfo, error) { 208 if !reflect.DeepEqual(mock.txID, id) { 209 return nil, fmt.Errorf("Invalid batch ID") 210 } 211 212 previousBlockHash := pbftTypes.MessageDigest("Genesis") 213 if 0 < mock.blockHeight { 214 previousBlock, _ := mock.GetState(mock.blockHeight - 1) 215 previousBlockHash, _ = mock.HashBlock(previousBlock) 216 } 217 218 block := &message.Block{ 219 ConsensusMetadata: metadata, 220 PreviousBlockHash: previousBlockHash, 221 StateResults: mock.curResults, // Use the current result output in the hash 222 Tasks: mock.curBatch, 223 } 224 225 if !preview { 226 hash, _ := mock.HashBlock(&message.StateInfo{Number : mock.blockHeight,Payload:block}) 227 fmt.Printf("TEST LEDGER: Mock ledger is inserting block %d with hash %x\n", mock.blockHeight, hash) 228 mock.blocks[mock.blockHeight] = &message.StateInfo{Number : mock.blockHeight,Hash:hash,Payload:block} 229 mock.blockHeight++ 230 } 231 232 return mock.blocks[mock.blockHeight], nil 233 } 234 235 func (mock *MockLedger) PreviewCommitTaskBatch(id interface{}, metadata []byte) ([]byte, error) { 236 b, err := mock.commonCommitTx(id, metadata, true) 237 if err != nil { 238 return nil, err 239 } 240 return mock.getBlockInfoBlob(mock.blockHeight+1, b), nil 241 } 242 243 func (mock *MockLedger) RollbackTaskBatch(id interface{}) error { 244 if !reflect.DeepEqual(mock.txID, id) { 245 return fmt.Errorf("Invalid batch ID") 246 } 247 mock.curBatch = nil 248 mock.curResults = nil 249 mock.txID = nil 250 return nil 251 } 252 253 func (mock *MockLedger) GetBlockchainSize() uint64 { 254 mock.mutex.Lock() 255 defer func() { 256 mock.mutex.Unlock() 257 }() 258 return mock.blockHeight 259 } 260 261 func (mock *MockLedger) GetState(id uint64) (*message.StateInfo, error) { 262 mock.mutex.Lock() 263 defer func() { 264 mock.mutex.Unlock() 265 }() 266 block, ok := mock.blocks[id] 267 if !ok { 268 return nil, fmt.Errorf("Block not found") 269 } 270 return block, nil 271 } 272 273 func (mock *MockLedger) HashBlock(block *message.StateInfo) (pbftTypes.MessageDigest, error) { 274 block.Hash = singletons.Hasher.Hash(block) 275 return block.Hash,nil 276 } 277 278 func (mock *MockLedger) GetStateInfo() *message.StateInfo { 279 b, _ := mock.GetState(mock.blockHeight - 1) 280 return b 281 } 282 283 func (mock *MockLedger) GetBlockchainInfoBlob() []byte { 284 b, _ := mock.GetState(mock.blockHeight - 1) 285 return mock.getBlockInfoBlob(mock.blockHeight, b) 286 } 287 288 func (mock *MockLedger) getBlockInfoBlob(height uint64, block *message.StateInfo) []byte { 289 h, _ := singletons.Marshaler.Marshal(block) 290 return h 291 } 292 293 func (mock *MockLedger) GetBlockHeadMetadata() ([]byte, error) { 294 b, ok := mock.blocks[mock.blockHeight-1] 295 if !ok { 296 return nil, fmt.Errorf("could not retrieve block from mock ledger") 297 } 298 block := b.Payload.(*message.Block) 299 return block.ConsensusMetadata, nil 300 } 301 302 func (mock *MockLedger) simulateStateTransfer(info *message.StateInfo, peers []*pbftTypes.PeerID) { 303 if mock.blockHeight >= info.Height() && info.Height()>0 { 304 blockCursor := info.Height() - 1 305 validHash := info.Digest() 306 for { 307 info, ok := mock.blocks[blockCursor] 308 if !ok { 309 break 310 } 311 hash, _ := mock.HashBlock(info) 312 if hash == validHash { 313 break 314 } 315 blockCursor-- 316 block := info.Payload.(*message.Block) 317 validHash = block.PreviousBlockHash 318 if blockCursor == ^uint64(0) { 319 return 320 } 321 } 322 panic(fmt.Sprintf("Asked to skip to a block (%d) which is lower than our current height of %d. (Corrupt block at %d with hash %x)", info.Height, mock.blockHeight, blockCursor, validHash)) 323 } 324 325 var remoteLedger consensus_pbft.ReadOnlyLedger 326 if len(peers) > 0 { 327 var ok bool 328 remoteLedger, ok = mock.remoteLedgers.GetLedgerByPeerID(peers[0]) 329 if !ok { 330 panic("Asked for results from a peer which does not exist") 331 } 332 } else { 333 // panic("TODO, support state transfer from nil peers") 334 } 335 fmt.Printf("TEST LEDGER skipping to %+v", info) 336 p := 0 337 for n := mock.blockHeight; n < info.Height(); n++ { 338 block, err := remoteLedger.GetState(n) 339 340 if nil != err { 341 n-- 342 fmt.Printf("TEST LEDGER: Block not ready yet") 343 time.Sleep(100 * time.Millisecond) 344 p++ 345 if p > 10 { 346 panic("Tried to get a block 10 times, no luck") 347 } 348 continue 349 } 350 351 mock.blocks[n] = block 352 } 353 mock.blockHeight = info.Height() 354 }