Update coordinator to work better under real net

- cli / node
    - Update handler of SIGINT so that after 3 SIGINTs, the process terminates
      unconditionally
- coordinator
    - Store stats without pointer
    - In all functions that send a variable via channel, check for context done
      to avoid deadlock (due to no process reading from the channel, which has
      no queue) when the node is stopped.
    - Abstract `canForge` so that it can be used outside of the `Coordinator`
    - In `canForge` check the blockNumber in current and next slot.
    - Update tests due to smart contract changes in slot handling, and minimum
      bid defaults
    - TxManager
        - Add consts, vars and stats to allow evaluating `canForge`
        - Add `canForge` method (not used yet)
        - Store batch and nonces status (last success and last pending)
        - Track nonces internally instead of relying on the ethereum node (this
          is required to work with ganache when there are pending txs)
        - Handle the (common) case of the receipt not being found after the tx
          is sent.
        - Don't start the main loop until we get an initial messae fo the stats
          and vars (so that in the loop the stats and vars are set to
          synchronizer values)
- eth / ethereum client
    - Add necessary methods to create the auth object for transactions manually
      so that we can set the nonce, gas price, gas limit, etc manually
    - Update `RollupForgeBatch` to take an auth object as input (so that the
      coordinator can set parameters manually)
- synchronizer
    - In stats, add `NextSlot`
This commit is contained in:
Eduard S
2021-01-15 19:37:39 +01:00
parent 168432c559
commit 70482605c4
20 changed files with 664 additions and 308 deletions

View File

@@ -233,12 +233,17 @@ func TestCoordinatorFlow(t *testing.T) {
// Bid for slot 2 and 4
_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
require.NoError(t, err)
_, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
bid, ok := new(big.Int).SetString("11000000000000000000", 10)
if !ok {
panic("bad bid")
}
_, err = ethClient.AuctionBidSimple(3, bid)
require.NoError(t, err)
_, err = ethClient.AuctionBidSimple(4, big.NewInt(9999))
_, err = ethClient.AuctionBidSimple(5, bid)
require.NoError(t, err)
coord.Start()
ctx := context.Background()
time.Sleep(1 * time.Second)
waitForSlot := func(slot int64) {
@@ -259,9 +264,17 @@ func TestCoordinatorFlow(t *testing.T) {
stats.Sync.LastBatch = stats.Eth.LastBatch
canForge, err := ethClient.AuctionCanForge(forger, blockNum+1)
require.NoError(t, err)
var slot common.Slot
slotNum := ethClientSetup.AuctionConstants.SlotNum(blockNum + 1)
slot.StartBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
(slotNum)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
slot.EndBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
(slotNum+1)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
if canForge {
stats.Sync.Auction.CurrentSlot.Forger = forger
slot.Forger = forger
}
stats.Sync.Auction.CurrentSlot = slot
// Copy stateDB to synchronizer if there was a new batch
source := fmt.Sprintf("%v/BatchNum%v", batchBuilderDBPath, stats.Sync.LastBatch)
dest := fmt.Sprintf("%v/BatchNum%v", syncDBPath, stats.Sync.LastBatch)
@@ -273,7 +286,7 @@ func TestCoordinatorFlow(t *testing.T) {
require.NoError(t, err)
}
}
coord.SendMsg(MsgSyncBlock{
coord.SendMsg(ctx, MsgSyncBlock{
Stats: stats,
})
}
@@ -329,7 +342,7 @@ func TestCoordCanForge(t *testing.T) {
if !ok {
panic("bad bid")
}
_, err = ethClient.AuctionBidSimple(2, bid)
_, err = ethClient.AuctionBidSimple(3, bid)
require.NoError(t, err)
modules2 := newTestModules(t)
@@ -343,28 +356,48 @@ func TestCoordCanForge(t *testing.T) {
var stats synchronizer.Stats
slots := [4]common.Slot{}
for i := 0; i < 4; i++ {
slots[i].StartBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
int64(i)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
slots[i].EndBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
int64(i+1)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
// Only slot 3 has Coordinator winner, the rest are BootCoordinator
if i == 3 {
slots[i].Forger = forger
} else {
slots[i].Forger = bootForger
}
}
// Slot 0. No bid, so the winner is the boot coordinator
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
stats.Sync.LastBlock = stats.Eth.LastBlock
stats.Sync.Auction.CurrentSlot.Forger = bootForger
assert.Equal(t, false, coord.canForge(&stats))
assert.Equal(t, true, bootCoord.canForge(&stats))
stats.Sync.Auction.CurrentSlot = slots[0]
coord.stats = stats
bootCoord.stats = stats
assert.Equal(t, false, coord.canForge())
assert.Equal(t, true, bootCoord.canForge())
// Slot 0. No bid, and we reach the deadline, so anyone can forge
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
int64(ethClientSetup.AuctionVariables.SlotDeadline)
stats.Sync.LastBlock = stats.Eth.LastBlock
stats.Sync.Auction.CurrentSlot.Forger = bootForger
assert.Equal(t, true, coord.canForge(&stats))
assert.Equal(t, true, bootCoord.canForge(&stats))
stats.Sync.Auction.CurrentSlot = slots[0]
coord.stats = stats
bootCoord.stats = stats
assert.Equal(t, true, coord.canForge())
assert.Equal(t, true, bootCoord.canForge())
// Slot 1. coordinator bid, so the winner is the coordinator
// Slot 3. coordinator bid, so the winner is the coordinator
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
3*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
stats.Sync.LastBlock = stats.Eth.LastBlock
stats.Sync.Auction.CurrentSlot.Forger = forger
assert.Equal(t, true, coord.canForge(&stats))
assert.Equal(t, false, bootCoord.canForge(&stats))
stats.Sync.Auction.CurrentSlot = slots[3]
coord.stats = stats
bootCoord.stats = stats
assert.Equal(t, true, coord.canForge())
assert.Equal(t, false, bootCoord.canForge())
}
func TestCoordHandleMsgSyncBlock(t *testing.T) {
@@ -382,49 +415,67 @@ func TestCoordHandleMsgSyncBlock(t *testing.T) {
if !ok {
panic("bad bid")
}
_, err = ethClient.AuctionBidSimple(2, bid)
_, err = ethClient.AuctionBidSimple(3, bid)
require.NoError(t, err)
slots := [4]common.Slot{}
for i := 0; i < 4; i++ {
slots[i].StartBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
int64(i)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
slots[i].EndBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
int64(i+1)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
// Only slot 3 has Coordinator winner, the rest are BootCoordinator
if i == 3 {
slots[i].Forger = forger
} else {
slots[i].Forger = bootForger
}
}
var msg MsgSyncBlock
stats := &msg.Stats
coord.stats = msg.Stats
ctx := context.Background()
// Slot 0. No bid, so the winner is the boot coordinator
// pipelineStarted: false -> false
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
stats.Sync.LastBlock = stats.Eth.LastBlock
stats.Sync.Auction.CurrentSlot.Forger = bootForger
assert.Equal(t, false, coord.canForge(stats))
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
coord.stats.Sync.Auction.CurrentSlot = slots[0]
assert.Equal(t, false, coord.canForge())
msg.Stats = coord.stats
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
assert.Nil(t, coord.pipeline)
// Slot 0. No bid, and we reach the deadline, so anyone can forge
// pipelineStarted: false -> true
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
int64(ethClientSetup.AuctionVariables.SlotDeadline)
stats.Sync.LastBlock = stats.Eth.LastBlock
stats.Sync.Auction.CurrentSlot.Forger = bootForger
assert.Equal(t, true, coord.canForge(stats))
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
coord.stats.Sync.Auction.CurrentSlot = slots[0]
assert.Equal(t, true, coord.canForge())
msg.Stats = coord.stats
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
assert.NotNil(t, coord.pipeline)
// Slot 0. No bid, and we reach the deadline, so anyone can forge
// pipelineStarted: true -> true
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
int64(ethClientSetup.AuctionVariables.SlotDeadline) + 1
stats.Sync.LastBlock = stats.Eth.LastBlock
stats.Sync.Auction.CurrentSlot.Forger = bootForger
assert.Equal(t, true, coord.canForge(stats))
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
coord.stats.Sync.Auction.CurrentSlot = slots[0]
assert.Equal(t, true, coord.canForge())
msg.Stats = coord.stats
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
assert.NotNil(t, coord.pipeline)
// Slot 0. No bid, so the winner is the boot coordinator
// Slot 1. No bid, so the winner is the boot coordinator
// pipelineStarted: true -> false
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
stats.Sync.LastBlock = stats.Eth.LastBlock
stats.Sync.Auction.CurrentSlot.Forger = bootForger
assert.Equal(t, false, coord.canForge(stats))
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
coord.stats.Sync.Auction.CurrentSlot = slots[1]
assert.Equal(t, false, coord.canForge())
msg.Stats = coord.stats
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
assert.Nil(t, coord.pipeline)
}
@@ -473,7 +524,7 @@ func TestCoordinatorStress(t *testing.T) {
require.NoError(t, err)
if blockData != nil {
stats := syn.Stats()
coord.SendMsg(MsgSyncBlock{
coord.SendMsg(ctx, MsgSyncBlock{
Stats: *stats,
Batches: blockData.Rollup.Batches,
Vars: synchronizer.SCVariablesPtr{