From ffdf34fbb2ed9b4531e74ed6f0e09fa63e2f7b1d Mon Sep 17 00:00:00 2001 From: Dapploper Date: Thu, 17 Jan 2019 03:38:23 +0900 Subject: [PATCH] pls: implement UAF feature and done with integrated test --- pls/backend.go | 3 +- pls/rootchain_manager.go | 418 +++++++++++++++++++-------------- pls/rootchain_manager_test.go | 430 ++++++++++++++++++++++++---------- 3 files changed, 552 insertions(+), 299 deletions(-) diff --git a/pls/backend.go b/pls/backend.go index 1d197c48a..db98b1ffc 100644 --- a/pls/backend.go +++ b/pls/backend.go @@ -472,7 +472,8 @@ func (s *Plasma) StartMining(threads int) error { // introduced to speed sync times. atomic.StoreUint32(&s.protocolManager.acceptTxs, 1) - go s.miner.Start(eb) + // TODO (aiden): there's no need to start miner in here, it starts when rcm connect to root chain contract by reading 1st NRE. + //go s.miner.Start(eb, &miner.NRE) } return nil } diff --git a/pls/rootchain_manager.go b/pls/rootchain_manager.go index fa736758c..dd908a5a5 100644 --- a/pls/rootchain_manager.go +++ b/pls/rootchain_manager.go @@ -69,6 +69,8 @@ type RootChainManager struct { // channels quit chan struct{} + exitCh chan struct{} + interruptCh chan struct{} epochPreparedCh chan *rootchain.RootChainEpochPrepared blockFinalizedCh chan *rootchain.RootChainBlockFinalized @@ -107,6 +109,8 @@ func NewRootChainManager( currentFork: big.NewInt(0), invalidExits: make(map[uint64]map[uint64]invalidExits), quit: make(chan struct{}), + exitCh: make(chan struct{}), + interruptCh: make(chan struct{}), epochPreparedCh: make(chan *rootchain.RootChainEpochPrepared, MAX_EPOCH_EVENTS), blockFinalizedCh: make(chan *rootchain.RootChainBlockFinalized), } @@ -167,6 +171,11 @@ func (rcm *RootChainManager) watchEvents() error { Context: context.Background(), } + watchOpts := &bind.WatchOpts{ + Context: context.Background(), + Start: &startBlockNumber, // read events from rootchain block#1 + } + // iterate previous events // TODO: the events fired while syncing should be dealt with in different way. iterator, err := filterer.FilterEpochPrepared(filterOpts) @@ -174,20 +183,38 @@ func (rcm *RootChainManager) watchEvents() error { return err } + // watch events from now + epochPrepareWatchCh := make(chan *rootchain.RootChainEpochPrepared) + epochPrepareSub, err := filterer.WatchEpochPrepared(watchOpts, epochPrepareWatchCh) + if err != nil { + return err + } + + log.Info("Watching EpochPrepared event", "startBlockNumber", startBlockNumber) + + iterator2, err := filterer.FilterBlockFinalized(filterOpts) + if err != nil { + return err + } + + blockFinalizedWatchCh := make(chan *rootchain.RootChainBlockFinalized) + blockFinalizedSub, err := filterer.WatchBlockFinalized(watchOpts, blockFinalizedWatchCh) + if err != nil { + return err + } + + log.Info("watching BlockFinalized event", "startBlockNumber", startBlockNumber) + log.Info("Iterating EpochPrepared event") for iterator.Next() { e := iterator.Event if e != nil { + log.Error("EpochPrepared event iterated") rcm.handleEpochPrepared(e) } } - iterator2, err := filterer.FilterBlockFinalized(filterOpts) - if err != nil { - return err - } - log.Info("Iterating BlockFinalized event") for iterator2.Next() { @@ -197,33 +224,15 @@ func (rcm *RootChainManager) watchEvents() error { } } - // watch events from now - watchOpts := &bind.WatchOpts{ - Context: context.Background(), - Start: &startBlockNumber, // read events from rootchain block#1 - } - - epochPrepareWatchCh := make(chan *rootchain.RootChainEpochPrepared) - epochPrepareSub, err := filterer.WatchEpochPrepared(watchOpts, epochPrepareWatchCh) - if err != nil { - return err - } - - log.Info("Watching EpochPrepared event", "startBlockNumber", startBlockNumber) - - blockFinalizedWatchCh := make(chan *rootchain.RootChainBlockFinalized) - blockFinalizedSub, err := filterer.WatchBlockFinalized(watchOpts, blockFinalizedWatchCh) - if err != nil { - return err - } - - log.Info("watching BlockFinalized event", "startBlockNumber", startBlockNumber) - go func() { for { select { case e := <-epochPrepareWatchCh: if e != nil { + // interrupt ORE in case of URE is prepared + if e.UserActivated && rcm.minerEnv.IsRequest { + rcm.interruptCh <- struct{}{} + } rcm.epochPreparedCh <- e } @@ -273,7 +282,7 @@ func (rcm *RootChainManager) runSubmitter() { select { case ev := <-plasmaBlockMinedEvents.Chan(): if ev == nil { - return + continue } // if the epoch is completed, stop mining operation and wait next epoch if rcm.minerEnv.Completed { @@ -281,12 +290,10 @@ func (rcm *RootChainManager) runSubmitter() { } blockInfo := ev.Data.(core.NewMinedBlockEvent) - // if the block is not URB in case of URB epoch, don't submit it - rcm.minerEnv.Lock.Lock() - if blockInfo.IsURB != rcm.minerEnv.IsUserActivated { - return + // if fork number of the block is not same as minerEnv, don't submit it. (double-check) + if blockInfo.Block.Difficulty().Cmp(rcm.minerEnv.CurrentFork) != 0 { + continue } - rcm.minerEnv.Lock.Unlock() rcm.lock.Lock() @@ -300,8 +307,13 @@ func (rcm *RootChainManager) runSubmitter() { } funcName := "submitNRB" - if rcm.minerEnv.IsRequest { + submitCost := rcm.state.costNRB + if rcm.minerEnv.UserActivated { + funcName = "submitURB" + submitCost = rcm.state.costURB + } else if rcm.minerEnv.IsRequest { funcName = "submitORB" + submitCost = rcm.state.costORB } input, err := rootchainContractABI.Pack( @@ -315,13 +327,15 @@ func (rcm *RootChainManager) runSubmitter() { if err != nil { log.Error("Failed to pack "+funcName, "err", err) } - submitTx := types.NewTransaction(Nonce, rcm.config.RootChainContract, big.NewInt(int64(rcm.state.costNRB)), params.SubmitBlockGasLimit, params.SubmitBlockGasPrice, input) + submitTx := types.NewTransaction(Nonce, rcm.config.RootChainContract, big.NewInt(int64(submitCost)), params.SubmitBlockGasLimit, params.SubmitBlockGasPrice, input) signedTx, err := w.SignTx(rcm.config.Operator, submitTx, rootchainNetworkId) if err != nil { log.Error("Failed to sign "+funcName, "err", err) } + log.Info("TxHash check", "txHash", blockInfo.Block.Header().TxHash) + err = rcm.backend.SendTransaction(context.Background(), signedTx) if err != nil { log.Error("Failed to send "+funcName, "err", err) @@ -369,38 +383,52 @@ func (rcm *RootChainManager) runHandlers() { // handleEpochPrepared handles EpochPrepared event from RootChain contract after // plasma chain is *SYNCED*. -// TODO (aiden): change call method(rcm.rootchaincontract.[someMethod(baseCallOpt, ...]) according to the change in the rootchain contract. // TODO (aiden): merge ORB, rebased ORB and URB logic if possible func (rcm *RootChainManager) handleEpochPrepared(ev *rootchain.RootChainEpochPrepared) error { rcm.lock.Lock() defer rcm.lock.Unlock() - events := rcm.eventMux.Subscribe(core.NewMinedBlockEvent{}) - defer events.Unsubscribe() - e := *ev - log.Info("RootChain epoch prepared", "epochNumber", e.EpochNumber, "isRequest", e.IsRequest, "userActivated", e.UserActivated, "isEmpty", e.EpochIsEmpty) + if e.EpochIsEmpty { + log.Info("epoch is empty, jump to next epoch") + return nil + } // stop miner immediately in case of URB epoch if e.UserActivated { rcm.miner.Stop() } + length := new(big.Int).Add(new(big.Int).Sub(e.EndBlockNumber, e.StartBlockNumber), big.NewInt(1)) + // start miner - go rcm.miner.Start(params.Operator, &e) + if !e.Rebase || e.IsRequest { + log.Info("RootChain epoch prepared", "epochNumber", e.EpochNumber, "epochLength", length, "isRequest", e.IsRequest, "userActivated", e.UserActivated, "isEmpty", e.EpochIsEmpty, "ForkNumber", e.ForkNumber, "isRebase", e.Rebase) + go rcm.miner.Start(params.Operator, &e) + } + + events := rcm.eventMux.Subscribe(core.NewMinedBlockEvent{}) + defer events.Unsubscribe() // prepare txs for the epoch - switch e.UserActivated { - case false: - // ORB, rebased ORB - if e.IsRequest && !e.EpochIsEmpty { - numORBs := new(big.Int).Sub(e.EndBlockNumber, e.StartBlockNumber) - numORBs = new(big.Int).Add(numORBs, big.NewInt(1)) + if e.IsRequest { + if e.UserActivated { + // URE - bodies := make([]types.Transactions, 0, numORBs.Uint64()) // [][]types.Transaction + if e.EpochIsEmpty { + log.Error("URB epoch is empty") + return nil + } + rcm.currentFork = new(big.Int).Add(rcm.currentFork, big.NewInt(1)) + rcm.state.currentFork++ - log.Debug("Num Orbs", "epochNumber", e.EpochNumber, "numORBs", numORBs, "e.EndBlockNumber", e.EndBlockNumber, "e.StartBlockNumber", e.StartBlockNumber) + numURBs := new(big.Int).Sub(e.EndBlockNumber, e.StartBlockNumber) + numURBs = new(big.Int).Add(numURBs, big.NewInt(1)) + + bodies := make([]types.Transactions, 0, numURBs.Uint64()) // [][]types.Transaction + + log.Debug("Num URBs", "epochNumber", e.EpochNumber, "numURBs", numURBs, "e.EndBlockNumber", e.EndBlockNumber, "e.StartBlockNumber", e.StartBlockNumber) currentFork := big.NewInt(int64(rcm.state.currentFork)) epoch, err := rcm.getEpoch(currentFork, e.EpochNumber) @@ -412,18 +440,19 @@ func (rcm *RootChainManager) handleEpochPrepared(ev *rootchain.RootChainEpochPre requestBlockId := big.NewInt(int64(epoch.FirstRequestBlockId)) for blockNumber := e.StartBlockNumber; blockNumber.Cmp(e.EndBlockNumber) <= 0; { - orb, err := rcm.rootchainContract.ORBs(baseCallOpt, requestBlockId) + urb, err := rcm.rootchainContract.URBs(baseCallOpt, requestBlockId) + if err != nil { return err } - numRequests := orb.RequestEnd - orb.RequestStart + 1 - log.Debug("Fetching ORB", "requestBlockId", requestBlockId, "numRequests", numRequests) + numRequests := urb.RequestEnd - urb.RequestStart + 1 + log.Debug("Fetching URB", "requestBlockId", requestBlockId, "numRequests", numRequests) body := make(types.Transactions, 0, numRequests) - for requestId := orb.RequestStart; requestId <= orb.RequestEnd; { - request, err := rcm.rootchainContract.EROs(baseCallOpt, big.NewInt(int64(requestId))) + for requestId := urb.RequestStart; requestId <= urb.RequestEnd; { + request, err := rcm.rootchainContract.ERUs(baseCallOpt, big.NewInt(int64(requestId))) if err != nil { return err } @@ -452,43 +481,44 @@ func (rcm *RootChainManager) handleEpochPrepared(ev *rootchain.RootChainEpochPre } requestTx := types.NewTransaction(0, to, request.Value, params.RequestTxGasLimit, params.RequestTxGasPrice, input) - log.Debug("Request Transaction", "tx", requestTx) - eroBytes, err := rcm.rootchainContract.GetEROBytes(baseCallOpt, big.NewInt(int64(requestId))) + // TODO (aiden): add GetERUBytes method in root chain contract + eruBytes, err := rcm.rootchainContract.GetEROBytes(baseCallOpt, big.NewInt(int64(requestId))) + if err != nil { - log.Error("Failed to get ERO bytes", "err", err) + log.Error("Failed to get ERU bytes", "err", err) } // TODO: check only in test - if !bytes.Equal(eroBytes, requestTx.GetRlp()) { - log.Error("ERO TX and request tx are different", "requestId", requestId, "eroBytes", common.Bytes2Hex(eroBytes), "requestTx.GetRlp()", common.Bytes2Hex(requestTx.GetRlp())) - } + if !bytes.Equal(eruBytes, requestTx.GetRlp()) { + log.Error("ERU TX and request tx are different", "requestId", requestId, "eruBytes", common.Bytes2Hex(eruBytes), "requestTx.GetRlp()", common.Bytes2Hex(requestTx.GetRlp())) - body = append(body, requestTx) + body = append(body, requestTx) - requestId += 1 - } + requestId += 1 + } - log.Info("Request txs fetched", "blockNumber", blockNumber, "requestBlockId", requestBlockId, "body", body) + log.Info("Request txs fetched", "blockNumber", blockNumber, "requestBlockId", requestBlockId, "body", body) - bodies = append(bodies, body) + bodies = append(bodies, body) - blockNumber = new(big.Int).Add(blockNumber, big.NewInt(1)) - requestBlockId = new(big.Int).Add(requestBlockId, big.NewInt(1)) + blockNumber = new(big.Int).Add(blockNumber, big.NewInt(1)) + requestBlockId = new(big.Int).Add(requestBlockId, big.NewInt(1)) + } } - var numMinedORBs uint64 = 0 + var numMinedURBs uint64 = 0 - for numMinedORBs < numORBs.Uint64() { - rcm.txPool.EnqueueRequestTxs(bodies[numMinedORBs]) + for numMinedURBs < numURBs.Uint64() { + rcm.txPool.EnqueueRequestTxs(bodies[numMinedURBs]) - log.Info("Waiting new request block mined event...") + log.Info("Waiting new user request block mined event...") e := <-events.Chan() block := e.Data.(core.NewMinedBlockEvent).Block - log.Info("New request block is mined", "block", block) + log.Info("New user request block is mined", "block", block) if !block.IsRequest() { return errors.New("Invalid request block type.") @@ -498,130 +528,96 @@ func (rcm *RootChainManager) handleEpochPrepared(ev *rootchain.RootChainEpochPre for _, receipt := range receipts { if receipt.Status == 0 { - log.Error("Request transaction is reverted", "blockNumber", block.Number(), "hash", receipt.TxHash) + log.Error("User request transaction is reverted", "blockNumber", block.Number(), "hash", receipt.TxHash) } } - - numMinedORBs += 1 - } - } else { - // NRB - switch e.Rebase { - case false: - // ordinary NRB - return nil - case true: - // rebased NRB - numNRBs := new(big.Int).Sub(e.EndBlockNumber, e.StartBlockNumber) - numNRBs = new(big.Int).Add(numNRBs, big.NewInt(1)) - - bodies := make([]types.Transactions, 0, numNRBs.Uint64()) // [][]types.Transaction - - log.Debug("Num Nrbs", "epochNumber", e.EpochNumber, "numNRBs", numNRBs, "e.EndBlockNumber", e.EndBlockNumber, "e.StartBlockNumber", e.StartBlockNumber) - - for blockNumber := e.StartBlockNumber; blockNumber.Cmp(e.EndBlockNumber) <= 0; { - //TODO (aiden): should change to new method 'GetBlockByForkAndNumber' - block := rcm.blockchain.GetBlockByNumber(blockNumber.Uint64()) - - txs := block.Transactions() - bodies = append(bodies, txs) - blockNumber = new(big.Int).Add(blockNumber, big.NewInt(1)) - } - - var numMinedNRBs uint64 = 0 - - for numMinedNRBs < numNRBs.Uint64() { - rcm.txPool.EnqueueRebasedTxs(bodies[numMinedNRBs]) - - log.Info("Waiting new block mined event...") - - <-events.Chan() - - numMinedNRBs += 1 - } + numMinedURBs += 1 } + return nil } + // ORE, rebase ORE + if !e.EpochIsEmpty { + // cancel ORE in case of URE is prepared + go func() { + for { + select { + case <-rcm.interruptCh: + return + case <-rcm.exitCh: + return + } + } + }() - case true: - // prepare request tx for URBs - if !e.IsRequest { - log.Error("URB epoch is non-request epoch") - } - - if e.EpochIsEmpty { - log.Error("URB epoch is empty") - } - - numURBs := new(big.Int).Sub(e.EndBlockNumber, e.StartBlockNumber) - numURBs = new(big.Int).Add(numURBs, big.NewInt(1)) - - bodies := make([]types.Transactions, 0, numURBs.Uint64()) // [][]types.Transaction - - log.Debug("Num URBs", "epochNumber", e.EpochNumber, "numURBs", numURBs, "e.EndBlockNumber", e.EndBlockNumber, "e.StartBlockNumber", e.StartBlockNumber) - - currentFork := big.NewInt(int64(rcm.state.currentFork)) - epoch, err := rcm.getEpoch(currentFork, e.EpochNumber) - if err != nil { - return err - } + numORBs := new(big.Int).Sub(e.EndBlockNumber, e.StartBlockNumber) + numORBs = new(big.Int).Add(numORBs, big.NewInt(1)) - // TODO: URE, ORE' should handle requestBlockId in a different way. - requestBlockId := big.NewInt(int64(epoch.FirstRequestBlockId)) - for blockNumber := e.StartBlockNumber; blockNumber.Cmp(e.EndBlockNumber) <= 0; { + bodies := make([]types.Transactions, 0, numORBs.Uint64()) // [][]types.Transaction - urb, err := rcm.rootchainContract.URBs(baseCallOpt, requestBlockId) + log.Debug("Num Orbs", "epochNumber", e.EpochNumber, "numORBs", numORBs, "e.EndBlockNumber", e.EndBlockNumber, "e.StartBlockNumber", e.StartBlockNumber) + currentFork := big.NewInt(int64(rcm.state.currentFork)) + epoch, err := rcm.getEpoch(currentFork, e.EpochNumber) if err != nil { return err } - numRequests := urb.RequestEnd - urb.RequestStart + 1 - log.Debug("Fetching URB", "requestBlockId", requestBlockId, "numRequests", numRequests) - - body := make(types.Transactions, 0, numRequests) + // TODO: URE, ORE' should handle requestBlockId in a different way. + requestBlockId := big.NewInt(int64(epoch.FirstRequestBlockId)) + for blockNumber := e.StartBlockNumber; blockNumber.Cmp(e.EndBlockNumber) <= 0; { - for requestId := urb.RequestStart; requestId <= urb.RequestEnd; { - request, err := rcm.rootchainContract.ERUs(baseCallOpt, big.NewInt(int64(requestId))) + orb, err := rcm.rootchainContract.ORBs(baseCallOpt, requestBlockId) if err != nil { return err } - log.Debug("Request fetched", "requestId", requestId, "hash", common.Bytes2Hex(request.Hash[:]), "request", request) - - var to common.Address - var input []byte - - if request.IsTransfer { - to = request.Requestor - } else { - to, _ = rcm.rootchainContract.RequestableContracts(baseCallOpt, request.To) - input, err = requestableContractABI.Pack("applyRequestInChildChain", - request.IsExit, - big.NewInt(int64(requestId)), - request.Requestor, - request.TrieKey, - request.TrieValue, - ) + numRequests := orb.RequestEnd - orb.RequestStart + 1 + log.Debug("Fetching ORB", "requestBlockId", requestBlockId, "numRequests", numRequests) + + body := make(types.Transactions, 0, numRequests) + + for requestId := orb.RequestStart; requestId <= orb.RequestEnd; { + request, err := rcm.rootchainContract.EROs(baseCallOpt, big.NewInt(int64(requestId))) if err != nil { - log.Error("Failed to pack applyRequestInChildChain", "err", err) + return err } - log.Debug("Request tx.data", "payload", input) - } + log.Debug("Request fetched", "requestId", requestId, "hash", common.Bytes2Hex(request.Hash[:]), "request", request) + + var to common.Address + var input []byte + + if request.IsTransfer { + to = request.Requestor + } else { + to, _ = rcm.rootchainContract.RequestableContracts(baseCallOpt, request.To) + input, err = requestableContractABI.Pack("applyRequestInChildChain", + request.IsExit, + big.NewInt(int64(requestId)), + request.Requestor, + request.TrieKey, + request.TrieValue, + ) + if err != nil { + log.Error("Failed to pack applyRequestInChildChain", "err", err) + } - requestTx := types.NewTransaction(0, to, request.Value, params.RequestTxGasLimit, params.RequestTxGasPrice, input) - log.Debug("Request Transaction", "tx", requestTx) + log.Debug("Request tx.data", "payload", input) + } - // TODO (aiden): add GetERUBytes method in root chain contract - eruBytes, err := rcm.rootchainContract.GetEROBytes(baseCallOpt, big.NewInt(int64(requestId))) + requestTx := types.NewTransaction(0, to, request.Value, params.RequestTxGasLimit, params.RequestTxGasPrice, input) - if err != nil { - log.Error("Failed to get ERO bytes", "err", err) - } + log.Debug("Request Transaction", "tx", requestTx) - // TODO: check only in test - if !bytes.Equal(eruBytes, requestTx.GetRlp()) { - log.Error("ERU TX and request tx are different", "requestId", requestId, "eruBytes", common.Bytes2Hex(eruBytes), "requestTx.GetRlp()", common.Bytes2Hex(requestTx.GetRlp())) + eroBytes, err := rcm.rootchainContract.GetEROBytes(baseCallOpt, big.NewInt(int64(requestId))) + if err != nil { + log.Error("Failed to get ERO bytes", "err", err) + } + + // TODO: check only in test + if !bytes.Equal(eroBytes, requestTx.GetRlp()) { + log.Error("ERO TX and request tx are different", "requestId", requestId, "eroBytes", common.Bytes2Hex(eroBytes), "requestTx.GetRlp()", common.Bytes2Hex(requestTx.GetRlp())) + } body = append(body, requestTx) @@ -636,10 +632,11 @@ func (rcm *RootChainManager) handleEpochPrepared(ev *rootchain.RootChainEpochPre requestBlockId = new(big.Int).Add(requestBlockId, big.NewInt(1)) } - var numMinedURBs uint64 = 0 + var numMinedORBs uint64 = 0 - for numMinedURBs < numURBs.Uint64() { - rcm.txPool.EnqueueRequestTxs(bodies[numMinedURBs]) + for numMinedORBs < numORBs.Uint64() { + + rcm.txPool.EnqueueRequestTxs(bodies[numMinedORBs]) log.Info("Waiting new request block mined event...") @@ -659,10 +656,77 @@ func (rcm *RootChainManager) handleEpochPrepared(ev *rootchain.RootChainEpochPre log.Error("Request transaction is reverted", "blockNumber", block.Number(), "hash", receipt.TxHash) } } - numMinedURBs += 1 + + numMinedORBs += 1 } + rcm.exitCh <- struct{}{} + return nil } } + + if e.Rebase { + // rebase NRE + n, err := rcm.rootchainContract.CurrentFork(baseCallOpt) + if err != nil { + log.Error("failed to get current fork number from root chain", "error", err) + } + + curFork, err := rcm.rootchainContract.Forks(baseCallOpt, n) + if err != nil { + log.Error("failed to get current fork data from root chain", "error", err) + } + preForkNum := new(big.Int).Sub(n, big.NewInt(1)) + + preFork, err := rcm.rootchainContract.Forks(baseCallOpt, preForkNum) + if err != nil { + log.Error("failed to get pre fork data from root chain", "error", err) + } + + var targets []uint64 + + // gather target NRBs to be rebased from pre fork. + for i := curFork.FirstBlock; i <= preFork.LastBlock; i++ { + block, err := rcm.rootchainContract.GetBlock(baseCallOpt, preForkNum, big.NewInt(int64(i))) + if err != nil { + log.Error("failed to get block from root chain", "error", err) + } + if !block.IsRequest { + targets = append(targets, i) + log.Info("gather target NRB to rebase", "number", i) + } + } + + bodies := make([]types.Transactions, 0, len(targets)) // [][]types.Transaction + + for i := 0; i < len(targets); i++ { + //TODO (aiden): should change to new method 'GetBlockByForkAndNumber' + block := rcm.blockchain.GetBlockByNumber(targets[i]) + txs := block.Transactions() + bodies = append(bodies, txs) + log.Info("get txs from target block", "number", block.Number()) + } + + endNum := e.StartBlockNumber.Int64() + int64(len(bodies)) - 1 + e.EndBlockNumber = big.NewInt(endNum) + length = big.NewInt(int64(len(bodies))) + + log.Info("RootChain epoch prepared", "epochNumber", e.EpochNumber, "epochLength", length, "isRequest", e.IsRequest, "userActivated", e.UserActivated, "isEmpty", e.EpochIsEmpty, "ForkNumber", e.ForkNumber, "isRebase", e.Rebase) + + rcm.miner.Start(params.Operator, &e) + + var numMinedNRBs uint64 = 0 + for numMinedNRBs < length.Uint64() { + rcm.txPool.EnqueueRebasedTxs(bodies[numMinedNRBs]) + + log.Info("Waiting new block mined event...") + <-events.Chan() + + numMinedNRBs += 1 + } + return nil + } + + // NRE return nil } @@ -677,6 +741,8 @@ func (rcm *RootChainManager) handleBlockFinalzied(ev *rootchain.RootChainBlockFi // send LastFinalizedBlock event to miner go rcm.eventMux.Post(miner.LastFinalizedBlock{Number: e.BlockNumber}) + rcm.minerEnv.SetLastFinalizedBlock(e.BlockNumber) + callerOpts := &bind.CallOpts{ Pending: true, Context: context.Background(), diff --git a/pls/rootchain_manager_test.go b/pls/rootchain_manager_test.go index 35ff6801f..0c06ab176 100644 --- a/pls/rootchain_manager_test.go +++ b/pls/rootchain_manager_test.go @@ -424,7 +424,7 @@ func TestScenario3(t *testing.T) { tokenInRootChain, tokenInChildChain, tokenAddrInRootChain, tokenAddrInChildChain := deployTokenContracts(t) wait(4) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -478,12 +478,12 @@ func TestScenario3(t *testing.T) { // NRBEpoch#1 / PlasmaBlock#2 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#2 / PlasmaBlock#3 (1/1): 4 ETH deposits - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -517,19 +517,19 @@ func TestScenario3(t *testing.T) { // NRBEpoch#3 / PlasmaBlock#4 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // NRBEpoch#3 / PlasmaBlock#5 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#4 / PlasmaBlock#6 (1/1): 1 Token deposit - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -554,7 +554,7 @@ func TestScenario3(t *testing.T) { // NRBEpoch#5 / PlasmaBlock#7 (1/2) transferToken(t, tokenInChildChain, key1, addr2, tokenAmountToTransfer) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -569,7 +569,7 @@ func TestScenario3(t *testing.T) { // NRBEpoch#5 / PlasmaBlock#8 (2/2) transferToken(t, tokenInChildChain, key1, addr2, tokenAmountToTransfer) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -584,7 +584,7 @@ func TestScenario3(t *testing.T) { // NRBEpoch#6 / PlasmaBlock#9 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -598,13 +598,13 @@ func TestScenario3(t *testing.T) { // NRBEpoch#6 / PlasmaBlock#10 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#7 / PlasmaBlock#11 (1/1) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -615,7 +615,7 @@ func TestScenario3(t *testing.T) { // NRBEpoch#8 / PlasmaBlock#12 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -624,12 +624,12 @@ func TestScenario3(t *testing.T) { // NRBEpoch#8 / PlasmaBlock#13 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#9 / PlasmaBlock#14 (1/1) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -640,7 +640,7 @@ func TestScenario3(t *testing.T) { // NRBEpoch#10 / PlasmaBlock#15 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -649,12 +649,12 @@ func TestScenario3(t *testing.T) { // NRBEpoch#10 / PlasmaBlock#16 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#11 / PlasmaBlock#17 (1/1) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -665,7 +665,7 @@ func TestScenario3(t *testing.T) { // NRBEpoch#12 / PlasmaBlock#18 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -674,12 +674,12 @@ func TestScenario3(t *testing.T) { // NRBEpoch#12 / PlasmaBlock#19 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#13 / PlasmaBlock#20 (1/1) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -690,13 +690,13 @@ func TestScenario3(t *testing.T) { // NRBEpoch#14 / PlasmaBlock#21 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // NRBEpoch#14 / PlasmaBlock#22 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -761,7 +761,7 @@ func TestScenario4(t *testing.T) { tokenInRootChain, tokenInChildChain, tokenAddrInRootChain, tokenAddrInChildChain := deployTokenContracts(t) wait(4) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -815,12 +815,12 @@ func TestScenario4(t *testing.T) { // NRBEpoch#1 / PlasmaBlock#2 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#2 / PlasmaBlock#3 (1/1): 4 ETH deposits - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -854,19 +854,19 @@ func TestScenario4(t *testing.T) { // NRBEpoch#3 / PlasmaBlock#4 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // NRBEpoch#3 / PlasmaBlock#5 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#4 / PlasmaBlock#6 (1/1): 1 Token deposit - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } @@ -891,48 +891,48 @@ func TestScenario4(t *testing.T) { // NRBEpoch#5 / PlasmaBlock#7 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // NRBEpoch#5 / PlasmaBlock#8 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // ORBEpoch#6 / PlasmaBlock#9 (1/1): invalid withdrawal - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { t.Fatal(err) } // NRBEpoch#7 / PlasmaBlock#10 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // NRBEpoch#7 / PlasmaBlock#11 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // NRBEpoch#8 / PlasmaBlock#12 (1/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } // NRBEpoch#8 / PlasmaBlock#12 (2/2) makeSampleTx(pls.rootchainManager) - if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false); err != nil { + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { t.Fatal(err) } @@ -950,6 +950,188 @@ func TestScenario4(t *testing.T) { applyRequests(t, pls.rootchainManager.rootchainContract, operatorKey) } +// NRB#1 - NRB#2 - (ORB#3) - NRB#3 - NRB#4 - ORB#5 - NRB#6 - NRB#7 - ORB#8 +// ㄴ URB#7 - (ORB#8(#8')) - NRB#8(#7') +func TestScenario5(t *testing.T) { + pls, rpcServer, dir, err := makePls() + defer os.RemoveAll(dir) + + if err != nil { + t.Fatalf("Failed to make pls service: %v", err) + } + defer pls.Stop() + defer rpcServer.Stop() + + // pls.Start() + pls.protocolManager.Start(1) + + //pls.StartMining(runtime.NumCPU()) + + rpcClient := rpc.DialInProc(rpcServer) + + // assign to global variable + plsClient = plsclient.NewClient(rpcClient) + + if err := pls.rootchainManager.Start(); err != nil { + t.Fatalf("Failed to start RootChainManager: %v", err) + } + + plasmaBlockMinedEvents := pls.rootchainManager.eventMux.Subscribe(core.NewMinedBlockEvent{}) + defer plasmaBlockMinedEvents.Unsubscribe() + + blockSubmitEvents := make(chan *rootchain.RootChainBlockSubmitted) + blockSubmitWatchOpts := &bind.WatchOpts{ + Start: nil, + Context: context.Background(), + } + blockFilterer, _ := pls.rootchainManager.rootchainContract.WatchBlockSubmitted(blockSubmitWatchOpts, blockSubmitEvents) + defer blockFilterer.Unsubscribe() + + log.Info("All backends are set up") + + tokenInRootChain, tokenInChildChain, tokenAddrInRootChain, tokenAddrInChildChain := deployTokenContracts(t) + + // NRBEpoch#0.1 / PlasmaBlock#0.1 (1/2) + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { + t.Fatal(err) + } + + opt := makeTxOpt(operatorKey, 0, nil, nil) + + _, err = tokenInRootChain.Mint(opt, addr1, ether(100)) + if err != nil { + t.Fatalf("Failed to mint token: %v", err) + } + wait(2) + + ts1, err := tokenInRootChain.TotalSupply(baseCallOpt) + if err != nil { + t.Fatalf("Failed to get total supply from root chain: %v", err) + } + + ts2, err := tokenInChildChain.TotalSupply(baseCallOpt) + if err != nil { + t.Fatalf("Failed to get total supply from child chain: %v", err) + } + + log.Info("Token total supply", "rootchain", ts1, "childchain", ts2) + + wait(3) + + _, err = pls.rootchainManager.rootchainContract.MapRequestableContractByOperator(opt, tokenAddrInRootChain, tokenAddrInChildChain) + if err != nil { + t.Fatalf("Failed to map token addresses to RootChain contract: %v", err) + } + wait(2) + + tokenAddr, err := pls.rootchainManager.rootchainContract.RequestableContracts(baseCallOpt, tokenAddrInRootChain) + wait(2) + if err != nil { + t.Fatalf("Failed to fetch token address from RootChain contract: %v", err) + } else if tokenAddr != tokenAddrInChildChain { + t.Fatalf("RootChain doesn't know requestable contract address in child chain: %v != %v", tokenAddrInChildChain, tokenAddr) + } + + // NRBEpoch#0.1 / PlasmaBlock#0.2 (2/2) + makeSampleTx(pls.rootchainManager) + + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { + t.Fatal(err) + } + + // deposit 1 ether for each account + //ETHBalances1 := getETHBalances(addrs) + //PETHBalances1 := getPETHBalances(addrs) + + depositAmount := ether(10) + //depositAmountNeg := new(big.Int).Neg(depositAmount) + + for _, key := range keys { + startETHDeposit(t, pls.rootchainManager, key, depositAmount) + } + + // deposit 1 token from addr1 + tokenAmount := ether(1) + tokenAmountNeg := new(big.Int).Neg(tokenAmount) + + TokenBalances1 := getTokenBalances(addrs, tokenInRootChain) + PTokenBalances1 := getTokenBalances(addrs, tokenInChildChain) + + startTokenDeposit(t, pls.rootchainManager, tokenInRootChain, tokenAddrInRootChain, key1, tokenAmount) + + // NRBEpoch#0.3 / PlasmaBlock#0.3 (1/2) + makeSampleTx(pls.rootchainManager) + + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { + t.Fatal(err) + } + + // NRBEpoch#0.3 / PlasmaBlock#0.4 (2/2) + makeSampleTx(pls.rootchainManager) + + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { + t.Fatal(err) + } + + // ORBEpoch#0.4 / PlasmaBlock#0.5 (1/1): 4 ETH deposits, deposit 1 token from addr1 (Enter) + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { + t.Fatal(err) + } + + TokenBalances2 := getTokenBalances(addrs, tokenInRootChain) + PTokenBalances2 := getTokenBalances(addrs, tokenInChildChain) + + // check Token balance + if err := checkBalance(TokenBalances1[0], TokenBalances2[0], tokenAmountNeg, nil, "Failed to check Token Balance (1)"); err != nil { + t.Fatal(err) + } + + // check PToken balance + if err := checkBalance(PTokenBalances1[0], PTokenBalances2[0], tokenAmount, nil, "Failed to check PToken Balance (1)"); err != nil { + t.Fatal(err) + } + + // withdraw 1 token from addr 1 + startTokenWithdraw(t, pls.rootchainManager.rootchainContract, tokenInRootChain, tokenAddrInRootChain, key1, tokenAmount, big.NewInt(int64(pls.rootchainManager.state.costERO))) + + // NRBEpoch#0.5 / PlasmaBlock#0.6 (1/2) + makeSampleTx(pls.rootchainManager) + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { + t.Fatal(err) + } + + // NRBEpoch#0.5 / PlasmaBlock#0.7 (2/2) + makeSampleTx(pls.rootchainManager) + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 0); err != nil { + t.Fatal(err) + } + + // make ERU + startTokenWithdrawWithERU(t, pls.rootchainManager.rootchainContract, tokenInRootChain, tokenAddrInRootChain, key1, tokenAmount, big.NewInt(int64(pls.rootchainManager.state.costERO))) + + // ORBEpoch#0.6 / PlasmaBlock#0.8 (1/1): 1 token withdrawal from addr1 (Exit) + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 0); err != nil { + t.Fatal(err) + } + + // Prepare URB + prepareToSubmitURB(t, pls.rootchainManager.rootchainContract, key1, big.NewInt(int64(pls.rootchainManager.state.costURBPrepare))) + + // Fork: URBEpoch#1.5 / PlasmaBlock#1.6 (1/1) / parent: PlasmaBlock#0.6 + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, true, 1); err != nil { + t.Fatal(err) + } + + // Rebased: ORBEpoch#1.6 / PlasmaBlock#1.7 (1/1): 1 ETH withdrawal (Exit) + // TODO (aiden): implement checkEpoch() to check empty epoch + + // Rebased: NRBEpoch#1.7 / PlasmaBlock#1.8 (1/2) / rebased block of 0.7 + // check rebased txs + if err := checkBlock(pls, plasmaBlockMinedEvents, blockSubmitEvents, false, 1); err != nil { + t.Fatal(err) + } +} + func startETHDeposit(t *testing.T, rcm *RootChainManager, key *ecdsa.PrivateKey, value *big.Int) { if value.Cmp(big.NewInt(0)) == 0 { t.Fatal("Cannot deposit 0 ETH") @@ -1077,6 +1259,61 @@ func startTokenWithdraw(t *testing.T, rootchainContract *rootchain.RootChain, to } } +func startTokenWithdrawWithERU(t *testing.T, rootchainContract *rootchain.RootChain, tokenContract *token.RequestableSimpleToken, tokenAddress common.Address, key *ecdsa.PrivateKey, amount, cost *big.Int) { + opt := makeTxOpt(key, 0, nil, cost) + addr := crypto.PubkeyToAddress(key.PublicKey) + + trieKey, err := tokenContract.GetBalanceTrieKey(baseCallOpt, addr) + if err != nil { + t.Fatalf("Failed to get trie key: %v", err) + } + trieValue := amount.Bytes() + trieValue = common.LeftPadBytes(trieValue, 32) + trieValue32Bytes := common.BytesToHash(trieValue) + + tx, err := rootchainContract.MakeERU(opt, tokenAddress, trieKey, trieValue32Bytes) + + if err != nil { + t.Fatalf("Failed to make an token withdrawal request: %v", err) + } + + wait(3) + + receipt, err := ethClient.TransactionReceipt(context.Background(), tx.Hash()) + if err != nil { + t.Fatalf("Failed to get receipt: %v", err) + } + if receipt == nil { + t.Fatalf("Failed to send transaction: %v", tx.Hash().Hex()) + } + if receipt.Status == 0 { + t.Fatalf("Transaction reverted: %v", tx.Hash().Hex()) + } +} + +func prepareToSubmitURB(t *testing.T, rootchainContract *rootchain.RootChain, key *ecdsa.PrivateKey, cost *big.Int) { + opt := makeTxOpt(key, 0, nil, cost) + + tx, err := rootchainContract.PrepareToSubmitURB(opt) + + if err != nil { + t.Fatalf("Failed to prepare to submit URB: %v", err) + } + + wait(3) + + receipt, err := ethClient.TransactionReceipt(context.Background(), tx.Hash()) + if err != nil { + t.Fatalf("Failed to get receipt: %v", err) + } + if receipt == nil { + t.Fatalf("Failed to send transaction: %v", tx.Hash().Hex()) + } + if receipt.Status == 0 { + t.Fatalf("Transaction reverted: %v", tx.Hash().Hex()) + } +} + func transferToken(t *testing.T, tokenContract *token.RequestableSimpleToken, key *ecdsa.PrivateKey, to common.Address, amount *big.Int) { opt := makeTxOpt(key, 0, nil, nil) _, err := tokenContract.Transfer(opt, to, amount) @@ -1380,14 +1617,14 @@ func makeManager() (*RootChainManager, func(), error) { mux := new(event.TypeMux) epochEnv := miner.NewEpochEnvironment() - miner := miner.New(minerBackend, params.PlasmaChainConfig, mux, engine, epochEnv, testPlsConfig.MinerRecommit, testPlsConfig.MinerGasFloor, testPlsConfig.MinerGasCeil, nil) + Miner := miner.New(minerBackend, params.PlasmaChainConfig, mux, engine, epochEnv, testPlsConfig.MinerRecommit, testPlsConfig.MinerGasFloor, testPlsConfig.MinerGasCeil, nil) var rcm *RootChainManager stopFn := func() { blockchain.Stop() txPool.Stop() - miner.Stop() + Miner.Stop() mux.Stop() rcm.Stop() } @@ -1400,15 +1637,15 @@ func makeManager() (*RootChainManager, func(), error) { rootchainContract, mux, nil, - miner, + Miner, epochEnv, ) if err != nil { return nil, func() {}, err } - - go miner.Start(operator) + // TODO (aiden): there's no need to start miner in here, it starts when rcm connect to root chain contract by reading 1st NRE. + //go Miner.Start(operator, &miner.NRE) return rcm, stopFn, nil } @@ -1472,7 +1709,7 @@ func makeSampleTx(rcm *RootChainManager) error { return nil } -func checkBlock(pls *Plasma, pbMinedEvents *event.TypeMuxSubscription, pbSubmitedEvents chan *rootchain.RootChainBlockSubmitted, expectedIsRequest bool) error { +func checkBlock(pls *Plasma, pbMinedEvents *event.TypeMuxSubscription, pbSubmitedEvents chan *rootchain.RootChainBlockSubmitted, expectedIsRequest bool, expectedFork int64) error { outC := make(chan struct{}) errC := make(chan error) defer close(outC) @@ -1489,95 +1726,38 @@ func checkBlock(pls *Plasma, pbMinedEvents *event.TypeMuxSubscription, pbSubmite }() go func() { - ev := <-pbMinedEvents.Chan() - <-pbSubmitedEvents - blockInfo := ev.Data.(core.NewMinedBlockEvent) + outC2 := make(chan struct{}) + defer close(outC2) + // use goroutine to read both events + var blockInfo core.NewMinedBlockEvent + go func() { + e := <-pbMinedEvents.Chan() + blockInfo = e.Data.(core.NewMinedBlockEvent) + outC2 <- struct{}{} + }() + + go func() { + <-pbSubmitedEvents + outC2 <- struct{}{} + }() + + <-outC2 + <-outC2 + block := blockInfo.Block - log.Info("Check PlasmaBlock Number", "localBlockNumber", pls.blockchain.CurrentBlock().NumberU64(), "minedBlockNumber", block.NumberU64()) + log.Warn("Check PlasmaBlock Number", "localBlockNumber", pls.blockchain.CurrentBlock().NumberU64(), "minedBlockNumber", block.NumberU64(), "forkNumber", block.Difficulty().Uint64()) // check block number. if pls.blockchain.CurrentBlock().NumberU64() != block.NumberU64() { errC <- errors.New(fmt.Sprintf("Expected block number: %d, actual block %d", block.NumberU64(), pls.blockchain.CurrentBlock().NumberU64())) + return } - // check isRequest. - if block.IsRequest() != expectedIsRequest { - log.Error("txs length check", "length", len(block.Transactions())) - - tx, _ := block.Transactions()[0].AsMessage(types.HomesteadSigner{}) - log.Error("tx sender address", "sender", tx.From()) - errC <- errors.New(fmt.Sprintf("Expected isRequest: %t, Actual isRequest %t", expectedIsRequest, block.IsRequest())) - } - - pb, _ := pls.rootchainManager.getBlock(big.NewInt(int64(pls.rootchainManager.state.currentFork)), block.Number()) - - if pb.Timestamp == 0 { - log.Debug("Submitted plasma block", "pb", pb) - log.Debug("Mined plasma block", "b", block) - errC <- errors.New("Plasma block is not submitted yet.") - } - - if pb.IsRequest != block.IsRequest() { - errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected isRequest: %t, Actual isRequest %t", pb.IsRequest, block.IsRequest())) - } - - pbStateRoot := pb.StatesRoot[:] - bStateRoot := block.Header().Root.Bytes() - if bytes.Compare(pbStateRoot, bStateRoot) != 0 { - errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected stateRoot: %s, Actual stateRoot: %s", pbStateRoot, bStateRoot)) - } - - pbTxRoot := pb.TransactionsRoot[:] - bTxRoot := block.Header().TxHash.Bytes() - if bytes.Compare(pbTxRoot, bTxRoot) != 0 { - errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected txRoot: %s, Actual txRoot: %s", pbTxRoot, bTxRoot)) - } - - pbReceiptsRoot := pb.ReceiptsRoot[:] - bReceiptsRoot := block.Header().ReceiptHash.Bytes() - if bytes.Compare(pbReceiptsRoot, bReceiptsRoot) != 0 { - errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected receiptsRoot: %s, Actual receiptsRoot: %s", pbReceiptsRoot, bReceiptsRoot)) - } - log.Debug("Check block finished") - outC <- struct{}{} - }() - - select { - case <-outC: - return nil - case err := <-errC: - return err - } -} - -func checkBlock2(pls *Plasma, pbMinedEvents *event.TypeMuxSubscription, pbSubmitedEvents chan *rootchain.RootChainBlockSubmitted, expectedIsRequest bool) error { - outC := make(chan struct{}) - errC := make(chan error) - defer close(outC) - defer close(errC) - - timer := time.NewTimer(4 * time.Second) - defer timer.Stop() - - go func() { - <-timer.C - if !timer.Stop() { - //errC <- errors.New("Out of time") - } - }() - - go func() { - <-pbSubmitedEvents - ev := <-pbMinedEvents.Chan() - blockInfo := ev.Data.(core.NewMinedBlockEvent) - block := blockInfo.Block - - log.Info("Check PlasmaBlock Number", "localBlockNumber", pls.blockchain.CurrentBlock().NumberU64(), "minedBlockNumber", block.NumberU64()) - - // check block number. - if pls.blockchain.CurrentBlock().NumberU64() != block.NumberU64() { - errC <- errors.New(fmt.Sprintf("Expected block number: %d, actual block %d", block.NumberU64(), pls.blockchain.CurrentBlock().NumberU64())) + // check fork number + if expectedFork != block.Difficulty().Int64() { + errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected ForkNumber: %d, Actual ForkNumber %d", expectedFork, block.Difficulty().Int64())) + return } // check isRequest. @@ -1587,6 +1767,7 @@ func checkBlock2(pls *Plasma, pbMinedEvents *event.TypeMuxSubscription, pbSubmit tx, _ := block.Transactions()[0].AsMessage(types.HomesteadSigner{}) log.Error("tx sender address", "sender", tx.From()) errC <- errors.New(fmt.Sprintf("Expected isRequest: %t, Actual isRequest %t", expectedIsRequest, block.IsRequest())) + return } pb, _ := pls.rootchainManager.getBlock(big.NewInt(int64(pls.rootchainManager.state.currentFork)), block.Number()) @@ -1595,28 +1776,33 @@ func checkBlock2(pls *Plasma, pbMinedEvents *event.TypeMuxSubscription, pbSubmit log.Debug("Submitted plasma block", "pb", pb) log.Debug("Mined plasma block", "b", block) errC <- errors.New("Plasma block is not submitted yet.") + return } if pb.IsRequest != block.IsRequest() { errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected isRequest: %t, Actual isRequest %t", pb.IsRequest, block.IsRequest())) + return } pbStateRoot := pb.StatesRoot[:] bStateRoot := block.Header().Root.Bytes() if bytes.Compare(pbStateRoot, bStateRoot) != 0 { errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected stateRoot: %s, Actual stateRoot: %s", pbStateRoot, bStateRoot)) + return } pbTxRoot := pb.TransactionsRoot[:] bTxRoot := block.Header().TxHash.Bytes() if bytes.Compare(pbTxRoot, bTxRoot) != 0 { errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected txRoot: %s, Actual txRoot: %s", pbTxRoot, bTxRoot)) + return } pbReceiptsRoot := pb.ReceiptsRoot[:] bReceiptsRoot := block.Header().ReceiptHash.Bytes() if bytes.Compare(pbReceiptsRoot, bReceiptsRoot) != 0 { errC <- errors.New(fmt.Sprintf("PlasmaBlock Expected receiptsRoot: %s, Actual receiptsRoot: %s", pbReceiptsRoot, bReceiptsRoot)) + return } log.Debug("Check block finished") outC <- struct{}{}