Fix next forgers api response

This commit is contained in:
Arnau B
2020-12-28 12:17:00 +01:00
parent e69970cd41
commit 6058d3c41a
10 changed files with 226 additions and 74 deletions

View File

@@ -177,6 +177,7 @@ type testCommon struct {
auctionVars common.AuctionVariables
rollupVars common.RollupVariables
wdelayerVars common.WDelayerVariables
nextForgers []NextForger
}
var tc testCommon
@@ -219,7 +220,6 @@ func TestMain(m *testing.M) {
// L2DB
l2DB := l2db.NewL2DB(database, 10, 100, 24*time.Hour)
test.WipeDB(l2DB.DB()) // this will clean HistoryDB and L2DB
// Config (smart contract constants)
_config := getConfigTest(chainID)
config = configAPI{
@@ -357,26 +357,128 @@ func TestMain(m *testing.M) {
panic(err)
}
// Generate Bids and add them to HistoryDB
const nBids = 20
commonBids := test.GenBids(nBids, commonBlocks, commonCoords)
if err = api.h.AddBids(commonBids); err != nil {
// Test next forgers
// Set auction vars
// Slots 3 and 6 will have bids that will be invalidated because of minBid update
// Slots 4 and 7 will have valid bids, the rest will be cordinator slots
var slot3MinBid int64 = 3
var slot4MinBid int64 = 4
var slot6MinBid int64 = 6
var slot7MinBid int64 = 7
// First update will indicate how things behave from slot 0
var defaultSlotSetBid [6]*big.Int = [6]*big.Int{
big.NewInt(10), // Slot 0 min bid
big.NewInt(10), // Slot 1 min bid
big.NewInt(10), // Slot 2 min bid
big.NewInt(slot3MinBid), // Slot 3 min bid
big.NewInt(slot4MinBid), // Slot 4 min bid
big.NewInt(10), // Slot 5 min bid
}
auctionVars := common.AuctionVariables{
EthBlockNum: int64(2),
DonationAddress: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
DefaultSlotSetBid: defaultSlotSetBid,
DefaultSlotSetBidSlotNum: 0,
Outbidding: uint16(1),
SlotDeadline: uint8(20),
BootCoordinator: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
BootCoordinatorURL: "https://boot.coordinator.io",
ClosedAuctionSlots: uint16(10),
OpenAuctionSlots: uint16(20),
}
if err := api.h.AddAuctionVars(&auctionVars); err != nil {
panic(err)
}
// Last update in auction vars will indicate how things will behave from slot 5
defaultSlotSetBid = [6]*big.Int{
big.NewInt(10), // Slot 5 min bid
big.NewInt(slot6MinBid), // Slot 6 min bid
big.NewInt(slot7MinBid), // Slot 7 min bid
big.NewInt(10), // Slot 8 min bid
big.NewInt(10), // Slot 9 min bid
big.NewInt(10), // Slot 10 min bid
}
auctionVars = common.AuctionVariables{
EthBlockNum: int64(3),
DonationAddress: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
DefaultSlotSetBid: defaultSlotSetBid,
DefaultSlotSetBidSlotNum: 5,
Outbidding: uint16(1),
SlotDeadline: uint8(20),
BootCoordinator: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
BootCoordinatorURL: "https://boot.coordinator.io",
ClosedAuctionSlots: uint16(10),
OpenAuctionSlots: uint16(20),
}
if err := api.h.AddAuctionVars(&auctionVars); err != nil {
panic(err)
}
// Generate SC vars and add them to HistoryDB (if needed)
var defaultSlotSetBid [6]*big.Int = [6]*big.Int{big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10)}
auctionVars := common.AuctionVariables{
EthBlockNum: int64(2),
DonationAddress: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
DefaultSlotSetBid: defaultSlotSetBid,
Outbidding: uint16(1),
SlotDeadline: uint8(20),
BootCoordinator: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
BootCoordinatorURL: "https://boot.coordinator.io",
ClosedAuctionSlots: uint16(2),
OpenAuctionSlots: uint16(5),
// Generate Bids and add them to HistoryDB
bids := []common.Bid{}
// Slot 1 and 2, no bids, wins boot coordinator
// Slot 3, below what's going to be the minimum (wins boot coordinator)
bids = append(bids, common.Bid{
SlotNum: 3,
BidValue: big.NewInt(slot3MinBid - 1),
EthBlockNum: commonBlocks[0].Num,
Bidder: commonCoords[0].Bidder,
})
// Slot 4, valid bid (wins bidder)
bids = append(bids, common.Bid{
SlotNum: 4,
BidValue: big.NewInt(slot4MinBid),
EthBlockNum: commonBlocks[0].Num,
Bidder: commonCoords[0].Bidder,
})
// Slot 5 no bids, wins boot coordinator
// Slot 6, below what's going to be the minimum (wins boot coordinator)
bids = append(bids, common.Bid{
SlotNum: 6,
BidValue: big.NewInt(slot6MinBid - 1),
EthBlockNum: commonBlocks[0].Num,
Bidder: commonCoords[0].Bidder,
})
// Slot 7, valid bid (wins bidder)
bids = append(bids, common.Bid{
SlotNum: 7,
BidValue: big.NewInt(slot7MinBid),
EthBlockNum: commonBlocks[0].Num,
Bidder: commonCoords[0].Bidder,
})
if err = api.h.AddBids(bids); err != nil {
panic(err)
}
bootForger := NextForger{
Coordinator: historydb.CoordinatorAPI{
Bidder: ethCommon.HexToAddress("0x0111111111111111111111111111111111111111"),
Forger: ethCommon.HexToAddress("0x0111111111111111111111111111111111111111"),
URL: "https://bootCoordinator",
},
}
// Set next forgers: set all as boot coordinator then replace the non boot coordinators
nextForgers := []NextForger{}
var initBlock int64 = 140
var deltaBlocks int64 = 40
for i := 1; i < int(auctionVars.ClosedAuctionSlots)+2; i++ {
fromBlock := initBlock + deltaBlocks*int64(i-1)
bootForger.Period = Period{
SlotNum: int64(i),
FromBlock: fromBlock,
ToBlock: fromBlock + deltaBlocks - 1,
}
nextForgers = append(nextForgers, bootForger)
}
// Set next forgers that aren't the boot coordinator
nonBootForger := historydb.CoordinatorAPI{
Bidder: commonCoords[0].Bidder,
Forger: commonCoords[0].Forger,
URL: commonCoords[0].URL,
}
// Slot 4
nextForgers[3].Coordinator = nonBootForger
// Slot 7
nextForgers[6].Coordinator = nonBootForger
var buckets [common.RollupConstNumBuckets]common.BucketParams
for i := range buckets {
@@ -386,6 +488,7 @@ func TestMain(m *testing.M) {
buckets[i].MaxWithdrawals = big.NewInt(int64(i) * 10000)
}
// Generate SC vars and add them to HistoryDB (if needed)
rollupVars := common.RollupVariables{
EthBlockNum: int64(3),
FeeAddToken: big.NewInt(100),
@@ -399,14 +502,9 @@ func TestMain(m *testing.M) {
WithdrawalDelay: uint64(3000),
}
err = api.h.AddAuctionVars(&auctionVars)
if err != nil {
panic(err)
}
// Generate test data, as expected to be received/sended from/to the API
testCoords := genTestCoordinators(commonCoords)
testBids := genTestBids(commonBlocks, testCoords, commonBids)
testBids := genTestBids(commonBlocks, testCoords, bids)
testExits := genTestExits(commonExitTree, testTokens, commonAccounts)
testTxs := genTestTxs(commonL1Txs, commonL2Txs, commonAccounts, testTokens, commonBlocks)
testBatches, testFullBatches := genTestBatches(commonBlocks, commonBatches, testTxs)
@@ -434,8 +532,11 @@ func TestMain(m *testing.M) {
auctionVars: auctionVars,
rollupVars: rollupVars,
wdelayerVars: wdelayerVars,
nextForgers: nextForgers,
}
// Run tests
result := m.Run()
// Fake server
if os.Getenv("FAKE_SERVER") == "yes" {
for {
@@ -443,8 +544,6 @@ func TestMain(m *testing.M) {
time.Sleep(30 * time.Second)
}
}
// Run tests
result := m.Run()
// Stop server
if err := server.Shutdown(context.Background()); err != nil {
panic(err)

View File

@@ -141,7 +141,7 @@ func TestGetBids(t *testing.T) {
err = doBadReq("GET", path, nil, 400)
assert.NoError(t, err)
// 404
path = fmt.Sprintf("%s?slotNum=%d&bidderAddr=%s", endpoint, tc.bids[0].SlotNum, tc.bids[1].Bidder.String())
path = fmt.Sprintf("%s?slotNum=%d&bidderAddr=%s", endpoint, 5, tc.bids[1].Bidder.String())
err = doBadReq("GET", path, nil, 404)
assert.NoError(t, err)
}

View File

@@ -62,13 +62,13 @@ func (a *API) genTestSlots(nSlots int, lastBlockNum int64, bids []testBid, aucti
return tSlots
}
func (a *API) getEmptyTestSlot(slotNum int64) testSlot {
firstBlock, lastBlock := a.getFirstLastBlock(slotNum)
func (a *API) getEmptyTestSlot(slotNum, lastBlock int64, auctionVars common.AuctionVariables) testSlot {
firstSlotBlock, lastSlotBlock := a.getFirstLastBlock(slotNum)
slot := testSlot{
SlotNum: slotNum,
FirstBlock: firstBlock,
LastBlock: lastBlock,
OpenAuction: false,
FirstBlock: firstSlotBlock,
LastBlock: lastSlotBlock,
OpenAuction: a.isOpenAuction(lastBlock, slotNum, auctionVars),
WinnerBid: nil,
}
return slot
@@ -98,7 +98,7 @@ func TestGetSlot(t *testing.T) {
nil, &fetchedSlot,
),
)
emptySlot := api.getEmptyTestSlot(slotNum)
emptySlot := api.getEmptyTestSlot(slotNum, api.status.Network.LastSyncBlock, tc.auctionVars)
assertSlot(t, emptySlot, fetchedSlot)
// Invalid slotNum
@@ -127,7 +127,7 @@ func TestGetSlots(t *testing.T) {
assert.NoError(t, err)
allSlots := tc.slots
for i := tc.slots[len(tc.slots)-1].SlotNum; i < maxSlotNum; i++ {
emptySlot := api.getEmptyTestSlot(i + 1)
emptySlot := api.getEmptyTestSlot(i+1, api.status.Network.LastSyncBlock, tc.auctionVars)
allSlots = append(allSlots, emptySlot)
}
assertSlots(t, allSlots, fetchedSlots)
@@ -148,7 +148,7 @@ func TestGetSlots(t *testing.T) {
// maxSlotNum & wonByEthereumAddress
fetchedSlots = []testSlot{}
limit = 1
bidderAddr := tc.coordinators[2].Bidder
bidderAddr := tc.coordinators[0].Bidder
path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d", endpoint, maxSlotNum, bidderAddr.String(), limit)
err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
assert.NoError(t, err)

View File

@@ -2,6 +2,7 @@ package api
import (
"database/sql"
"math/big"
"net/http"
"time"
@@ -122,6 +123,21 @@ func (a *API) getNextForgers(lastBlock common.Block, currentSlot, lastClosedSlot
return nil, tracerr.Wrap(err)
}
nextForgers := []NextForger{}
// Get min bid info
var minBidInfo []historydb.MinBidInfo
if currentSlot >= a.status.Auction.DefaultSlotSetBidSlotNum {
// All min bids can be calculated with the last update of AuctionVariables
minBidInfo = []historydb.MinBidInfo{{
DefaultSlotSetBid: a.status.Auction.DefaultSlotSetBid,
DefaultSlotSetBidSlotNum: a.status.Auction.DefaultSlotSetBidSlotNum,
}}
} else {
// Get all the relevant updates from the DB
minBidInfo, err = a.h.GetAuctionVarsUntilSetSlotNum(lastClosedSlot, int(lastClosedSlot-currentSlot)+1)
if err != nil {
return nil, tracerr.Wrap(err)
}
}
// Create nextForger for each slot
for i := currentSlot; i <= lastClosedSlot; i++ {
fromBlock := i*int64(a.cg.AuctionConstants.BlocksPerSlot) + a.cg.AuctionConstants.GenesisBlockNum
@@ -135,11 +151,35 @@ func (a *API) getNextForgers(lastBlock common.Block, currentSlot, lastClosedSlot
ToTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(toBlock-lastBlock.Num))),
},
}
foundBid := false
foundForger := false
// If there is a bid for a slot, get forger (coordinator)
for j := range bids {
if bids[j].SlotNum == i {
foundBid = true
slotNum := bids[j].SlotNum
if slotNum == i {
// There's a bid for the slot
// Check if the bid is greater than the minimum required
for i := 0; i < len(minBidInfo); i++ {
// Find the most recent update
if slotNum >= minBidInfo[i].DefaultSlotSetBidSlotNum {
// Get min bid
minBidSelector := slotNum % int64(len(a.status.Auction.DefaultSlotSetBid))
minBid := minBidInfo[i].DefaultSlotSetBid[minBidSelector]
// Check if the bid has beaten the minimum
bid, ok := new(big.Int).SetString(string(bids[j].BidValue), 10)
if !ok {
return nil, tracerr.New("Wrong bid value, error parsing it as big.Int")
}
if minBid.Cmp(bid) == 1 {
// Min bid is greater than bid, the slot will be forged by boot coordinator
break
}
foundForger = true
break
}
}
if !foundForger { // There is no bid or it's smaller than the minimum
break
}
coordinator, err := a.h.GetCoordinatorAPI(bids[j].Bidder)
if err != nil {
return nil, tracerr.Wrap(err)
@@ -149,7 +189,7 @@ func (a *API) getNextForgers(lastBlock common.Block, currentSlot, lastClosedSlot
}
}
// If there is no bid, the coordinator that will forge is boot coordinator
if !foundBid {
if !foundForger {
nextForger.Coordinator = bootCoordinator
}
nextForgers = append(nextForgers, nextForger)

View File

@@ -2,15 +2,12 @@ package api
import (
"testing"
"time"
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/db/historydb"
"github.com/stretchr/testify/assert"
)
const secondsPerBlock = 15
type testStatus struct {
Network testNetwork `json:"network"`
Metrics historydb.Metrics `json:"metrics"`
@@ -49,36 +46,6 @@ func TestSetAuctionVariables(t *testing.T) {
assert.Equal(t, tc.auctionVars, api.status.Auction)
}
func TestNextForgers(t *testing.T) {
// It's assumed that bids for each slot will be received in increasing order
bestBids := make(map[int64]testBid)
for j := range tc.bids {
bestBids[tc.bids[j].SlotNum] = tc.bids[j]
}
lastBlock := tc.blocks[len(tc.blocks)-1]
for i := int64(0); i < tc.slots[len(tc.slots)-1].SlotNum; i++ {
lastClosedSlot := i + int64(api.status.Auction.ClosedAuctionSlots)
nextForgers, err := api.getNextForgers(tc.blocks[len(tc.blocks)-1], i, lastClosedSlot)
assert.NoError(t, err)
for j := i; j <= lastClosedSlot; j++ {
for q := range nextForgers {
if nextForgers[q].Period.SlotNum == j {
if nextForgers[q].Coordinator.ItemID != 0 {
assert.Equal(t, bestBids[j].Bidder, nextForgers[q].Coordinator.Bidder)
} else {
assert.Equal(t, bootCoordinator.Bidder, nextForgers[q].Coordinator.Bidder)
}
firstBlockSlot, lastBlockSlot := api.getFirstLastBlock(j)
fromTimestamp := lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(firstBlockSlot-lastBlock.Num)))
toTimestamp := lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(lastBlockSlot-lastBlock.Num)))
assert.Equal(t, fromTimestamp.Unix(), nextForgers[q].Period.FromTimestamp.Unix())
assert.Equal(t, toTimestamp.Unix(), nextForgers[q].Period.ToTimestamp.Unix())
}
}
}
}
}
func TestUpdateNetworkInfo(t *testing.T) {
status := &Network{}
assert.Equal(t, status.LastSyncBlock, api.status.Network.LastSyncBlock)
@@ -143,23 +110,43 @@ func TestGetState(t *testing.T) {
var status testStatus
assert.NoError(t, doGoodReq("GET", endpoint, nil, &status))
// SC vars
assert.Equal(t, tc.rollupVars, status.Rollup)
assert.Equal(t, tc.auctionVars, status.Auction)
assert.Equal(t, tc.wdelayerVars, status.WithdrawalDelayer)
// Network
assert.Equal(t, lastBlock.Num, status.Network.LastEthBlock)
assert.Equal(t, lastBlock.Num, status.Network.LastSyncBlock)
// TODO: assert all the batch, not just the batch num
assert.Equal(t, lastBatchNum, status.Network.LastBatch.BatchNum)
assert.Equal(t, currentSlotNum, status.Network.CurrentSlot)
assert.Equal(t, int(api.status.Auction.ClosedAuctionSlots)+1, len(status.Network.NextForgers))
assertNextForgers(t, tc.nextForgers, status.Network.NextForgers)
// Metrics
// TODO: perform real asserts (not just greater than 0)
assert.Greater(t, status.Metrics.TransactionsPerBatch, float64(0))
assert.Greater(t, status.Metrics.BatchFrequency, float64(0))
assert.Greater(t, status.Metrics.TransactionsPerBatch, float64(0))
assert.Greater(t, status.Metrics.TotalAccounts, int64(0))
assert.Greater(t, status.Metrics.TotalBJJs, int64(0))
assert.Greater(t, status.Metrics.AvgTransactionFee, float64(0))
// Recommended fee
// TODO: perform real asserts (not just greater than 0)
assert.Greater(t, status.RecommendedFee.ExistingAccount, float64(0))
assert.Equal(t, status.RecommendedFee.CreatesAccount,
status.RecommendedFee.ExistingAccount*createAccountExtraFeePercentage)
assert.Equal(t, status.RecommendedFee.CreatesAccountAndRegister,
status.RecommendedFee.ExistingAccount*createAccountInternalExtraFeePercentage)
}
func assertNextForgers(t *testing.T, expected, actual []NextForger) {
assert.Equal(t, len(expected), len(actual))
for i := range expected {
// ignore timestamps and other metadata
actual[i].Period.FromTimestamp = expected[i].Period.FromTimestamp
actual[i].Period.ToTimestamp = expected[i].Period.ToTimestamp
actual[i].Coordinator.ItemID = expected[i].Coordinator.ItemID
actual[i].Coordinator.EthBlockNum = expected[i].Coordinator.EthBlockNum
assert.Equal(t, expected[i], actual[i])
}
}

View File

@@ -2714,6 +2714,9 @@ components:
items:
type: integer
example: [32,0,68,21,55,99]
defaultSlotSetBidSlotNum:
type: integer
description: Slot in which the changes will be applied for the first time.
outbidding:
type: number
description: Minimum outbid over the previous one to consider it valid.