diff --git a/eth/auction.go b/eth/auction.go index 0ea1778..cfcd8eb 100644 --- a/eth/auction.go +++ b/eth/auction.go @@ -62,12 +62,11 @@ type AuctionVariables struct { // AuctionState represents the state of the Rollup in the Smart Contract type AuctionState struct { // Mapping to control slot state - Slots map[int64]SlotState + Slots map[int64]*SlotState // Mapping to control balances pending to claim PendingBalances map[ethCommon.Address]*big.Int - // Mapping to register all the coordinators. The address used for the mapping is the forger address - Coordinators map[ethCommon.Address]Coordinator + Coordinators map[ethCommon.Address]*Coordinator } // AuctionEventNewBid is an event of the Auction Smart Contract diff --git a/test/ethclient.go b/test/ethclient.go index b9c07c6..236af61 100644 --- a/test/ethclient.go +++ b/test/ethclient.go @@ -6,6 +6,8 @@ import ( "encoding/json" "fmt" "math/big" + "reflect" + "sync" "time" ethCommon "github.com/ethereum/go-ethereum/common" @@ -18,18 +20,75 @@ import ( "github.com/mitchellh/copystructure" ) +func init() { + copystructure.Copiers[reflect.TypeOf(big.Int{})] = + func(raw interface{}) (interface{}, error) { + in := raw.(big.Int) + out := new(big.Int).Set(&in) + return *out, nil + } +} + // RollupBlock stores all the data related to the Rollup SC from an ethereum block type RollupBlock struct { - State eth.RollupState - Vars eth.RollupVariables - Events eth.RollupEvents -} + State eth.RollupState + Vars eth.RollupVariables + Events eth.RollupEvents + Constants *eth.RollupConstants + Eth *EthereumBlock +} + +var ( + errBidClosed = fmt.Errorf("Bid has already been closed") + errBidNotOpen = fmt.Errorf("Bid has not been opened yet") + errBidBelowMin = fmt.Errorf("Bid below minimum") + errCoordNotReg = fmt.Errorf("Coordinator not registered") +) // AuctionBlock stores all the data related to the Auction SC from an ethereum block type AuctionBlock struct { - State eth.AuctionState - Vars eth.AuctionVariables - Events eth.AuctionEvents + State eth.AuctionState + Vars eth.AuctionVariables + Events eth.AuctionEvents + Constants *eth.AuctionConstants + Eth *EthereumBlock +} + +func (a *AuctionBlock) getSlotNumber(blockNumber int64) int64 { + if a.Eth.BlockNum >= a.Constants.GenesisBlockNum { + return (blockNumber - a.Constants.GenesisBlockNum) / int64(a.Constants.BlocksPerSlot) + } + return 0 +} + +func (a *AuctionBlock) getCurrentSlotNumber() int64 { + return a.getSlotNumber(a.Eth.BlockNum) +} + +func (a *AuctionBlock) getEpoch(slot int64) int64 { + return slot % int64(len(a.Vars.MinBidEpoch)) +} + +func (a *AuctionBlock) getMinBidBySlot(slot int64) (*big.Int, error) { + if slot < a.getCurrentSlotNumber()+int64(a.Vars.ClosedAuctionSlots) { + return nil, errBidClosed + } + + epoch := a.getEpoch(slot) + var prevBid *big.Int + slotState, ok := a.State.Slots[slot] + // If the bidAmount for a slot is 0 it means that it has not yet been bid, so the midBid will be the minimum + // bid for the slot time plus the outbidding set, otherwise it will be the bidAmount plus the outbidding + if !ok || slotState.BidAmount.Cmp(big.NewInt(0)) == 0 { + prevBid = a.Vars.MinBidEpoch[epoch] + } else { + prevBid = slotState.BidAmount + } + outBid := new(big.Int).Set(prevBid) + outBid.Mul(outBid, big.NewInt(int64(a.Vars.Outbidding))) + outBid.Div(outBid, big.NewInt(100)) //nolint:gomnd + outBid.Add(prevBid, outBid) + return outBid, nil } // EthereumBlock stores all the generic data related to the an ethereum block @@ -48,17 +107,25 @@ type Block struct { Eth *EthereumBlock } -// type ethState struct { -// blockNum int64 -// } - -// type state struct { -// rollupState eth.RollupState -// rollupVars eth.RollupVariables -// auctionState eth.AuctionState -// auctionVars eth.AuctionVariables -// eth ethState -// } +// Next prepares the successive block. +func (b *Block) Next() *Block { + blockNextRaw, err := copystructure.Copy(b) + if err != nil { + panic(err) + } + blockNext := blockNextRaw.(*Block) + blockNext.Rollup.Events = eth.NewRollupEvents() + blockNext.Auction.Events = eth.NewAuctionEvents() + blockNext.Eth = &EthereumBlock{ + BlockNum: b.Eth.BlockNum + 1, + ParentHash: b.Eth.Hash, + } + blockNext.Rollup.Constants = b.Rollup.Constants + blockNext.Auction.Constants = b.Auction.Constants + blockNext.Rollup.Eth = blockNext.Eth + blockNext.Auction.Eth = blockNext.Eth + return blockNext +} // ClientSetup is used to initialize the constants of the Smart Contracts and // other details of the test Client @@ -70,6 +137,45 @@ type ClientSetup struct { VerifyProof bool } +// NewClientSetupExample returns a ClientSetup example with hardcoded realistic values. +// TODO: Fill all values that are currently default. +//nolint:gomnd +func NewClientSetupExample() *ClientSetup { + rollupConstants := ð.RollupConstants{} + rollupVariables := ð.RollupVariables{ + MaxTxVerifiers: make([]int, 0), + TokenHEZ: ethCommon.Address{}, + GovernanceAddress: ethCommon.Address{}, + SafetyBot: ethCommon.Address{}, + ConsensusContract: ethCommon.Address{}, + WithdrawalContract: ethCommon.Address{}, + FeeAddToken: big.NewInt(1), + ForgeL1Timeout: 16, + FeeL1UserTx: big.NewInt(2), + } + auctionConstants := ð.AuctionConstants{ + BlocksPerSlot: 40, + } + auctionVariables := ð.AuctionVariables{ + DonationAddress: ethCommon.Address{}, + BootCoordinator: ethCommon.Address{}, + MinBidEpoch: [6]*big.Int{ + big.NewInt(10), big.NewInt(11), big.NewInt(12), + big.NewInt(13), big.NewInt(14), big.NewInt(15)}, + ClosedAuctionSlots: 2, + OpenAuctionSlots: 100, + AllocationRatio: [3]uint8{}, + Outbidding: 10, + SlotDeadline: 20, + } + return &ClientSetup{ + RollupConstants: rollupConstants, + RollupVariables: rollupVariables, + AuctionConstants: auctionConstants, + AuctionVariables: auctionVariables, + } +} + // Timer is an interface to simulate a source of time, useful to advance time // virtually. type Timer interface { @@ -85,7 +191,9 @@ type Timer interface { // Client implements the eth.ClientInterface interface, allowing to manipulate the // values for testing, working with deterministic results. type Client struct { + rw *sync.RWMutex log bool + addr ethCommon.Address rollupConstants *eth.RollupConstants auctionConstants *eth.AuctionConstants blocks map[int64]*Block @@ -101,7 +209,7 @@ type Client struct { // NewClient returns a new test Client that implements the eth.IClient // interface, at the given initialBlockNumber. -func NewClient(l bool, timer Timer, setup *ClientSetup) *Client { +func NewClient(l bool, timer Timer, addr ethCommon.Address, setup *ClientSetup) *Client { blocks := make(map[int64]*Block) blockNum := int64(0) @@ -124,17 +232,19 @@ func NewClient(l bool, timer Timer, setup *ClientSetup) *Client { LastToForgeL1TxsNum: 1, CurrentIdx: 0, }, - Vars: *setup.RollupVariables, - Events: eth.NewRollupEvents(), + Vars: *setup.RollupVariables, + Events: eth.NewRollupEvents(), + Constants: setup.RollupConstants, }, Auction: &AuctionBlock{ State: eth.AuctionState{ - Slots: make(map[int64]eth.SlotState), + Slots: make(map[int64]*eth.SlotState), PendingBalances: make(map[ethCommon.Address]*big.Int), - Coordinators: make(map[ethCommon.Address]eth.Coordinator), + Coordinators: make(map[ethCommon.Address]*eth.Coordinator), }, - Vars: *setup.AuctionVariables, - Events: eth.NewAuctionEvents(), + Vars: *setup.AuctionVariables, + Events: eth.NewAuctionEvents(), + Constants: setup.AuctionConstants, }, Eth: &EthereumBlock{ BlockNum: blockNum, @@ -143,16 +253,16 @@ func NewClient(l bool, timer Timer, setup *ClientSetup) *Client { ParentHash: ethCommon.Hash{}, }, } + blockCurrent.Rollup.Eth = blockCurrent.Eth + blockCurrent.Auction.Eth = blockCurrent.Eth blocks[blockNum] = &blockCurrent - blockNextRaw, err := copystructure.Copy(&blockCurrent) - if err != nil { - panic(err) - } - blockNext := blockNextRaw.(*Block) + blockNext := blockCurrent.Next() blocks[blockNum+1] = blockNext return &Client{ + rw: &sync.RWMutex{}, log: l, + addr: addr, rollupConstants: setup.RollupConstants, auctionConstants: setup.AuctionConstants, blocks: blocks, @@ -197,29 +307,26 @@ func (c *Client) nextBlock() *Block { return c.blocks[c.blockNum+1] } +func (c *Client) currentBlock() *Block { + return c.blocks[c.blockNum] +} + // CtlMineBlock moves one block forward func (c *Client) CtlMineBlock() { + c.rw.Lock() + defer c.rw.Unlock() + blockCurrent := c.nextBlock() c.blockNum++ c.maxBlockNum = c.blockNum - blockCurrent.Eth = &EthereumBlock{ - BlockNum: c.blockNum, - Time: c.timer.Time(), - Hash: c.hasher.Next(), - ParentHash: blockCurrent.Eth.Hash, - } + blockCurrent.Eth.Time = c.timer.Time() + blockCurrent.Eth.Hash = c.hasher.Next() for ethTxHash, forgeBatchArgs := range c.forgeBatchArgsPending { c.forgeBatchArgs[ethTxHash] = forgeBatchArgs } c.forgeBatchArgsPending = make(map[ethCommon.Hash]*eth.RollupForgeBatchArgs) - blockNextRaw, err := copystructure.Copy(blockCurrent) - if err != nil { - panic(err) - } - blockNext := blockNextRaw.(*Block) - blockNext.Rollup.Events = eth.NewRollupEvents() - blockNext.Auction.Events = eth.NewAuctionEvents() + blockNext := blockCurrent.Next() c.blocks[c.blockNum+1] = blockNext c.Debugw("TestClient mined block", "blockNum", c.blockNum) } @@ -227,6 +334,9 @@ func (c *Client) CtlMineBlock() { // CtlRollback discards the last mined block. Use this to replace a mined // block to simulate reorgs. func (c *Client) CtlRollback() { + c.rw.Lock() + defer c.rw.Unlock() + if c.blockNum == 0 { panic("Can't rollback at blockNum = 0") } @@ -234,13 +344,7 @@ func (c *Client) CtlRollback() { delete(c.blocks, c.blockNum) // delete current block c.blockNum-- blockCurrent := c.blocks[c.blockNum] - blockNextRaw, err := copystructure.Copy(blockCurrent) - if err != nil { - panic(err) - } - blockNext := blockNextRaw.(*Block) - blockNext.Rollup.Events = eth.NewRollupEvents() - blockNext.Auction.Events = eth.NewAuctionEvents() + blockNext := blockCurrent.Next() c.blocks[c.blockNum+1] = blockNext } @@ -250,6 +354,9 @@ func (c *Client) CtlRollback() { // EthCurrentBlock returns the current blockNum func (c *Client) EthCurrentBlock() (int64, error) { + c.rw.RLock() + defer c.rw.RUnlock() + if c.blockNum < c.maxBlockNum { panic("blockNum has decreased. " + "After a rollback you must mine to reach the same or higher blockNum") @@ -273,6 +380,9 @@ func (c *Client) EthCurrentBlock() (int64, error) { // EthBlockByNumber returns the *common.Block for the given block number in a // deterministic way. func (c *Client) EthBlockByNumber(ctx context.Context, blockNum int64) (*common.Block, error) { + c.rw.RLock() + defer c.rw.RUnlock() + block, ok := c.blocks[blockNum] if !ok { return nil, fmt.Errorf("block not found") @@ -293,6 +403,9 @@ var errTODO = fmt.Errorf("TODO: Not implemented yet") // CtlAddL1TxUser adds an L1TxUser to the L1UserTxs queue of the Rollup func (c *Client) CtlAddL1TxUser(l1Tx *common.L1Tx) { + c.rw.Lock() + defer c.rw.Unlock() + nextBlock := c.nextBlock() r := nextBlock.Rollup queue := r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] @@ -311,8 +424,13 @@ func (c *Client) CtlAddL1TxUser(l1Tx *common.L1Tx) { r.Events.L1UserTx = append(r.Events.L1UserTx, eth.RollupEventL1UserTx{L1Tx: *l1Tx}) } +type transactionData struct { + Name string + Value interface{} +} + func (c *Client) newTransaction(name string, value interface{}) *types.Transaction { - data, err := json.Marshal(value) + data, err := json.Marshal(transactionData{name, value}) if err != nil { panic(err) } @@ -322,11 +440,17 @@ func (c *Client) newTransaction(name string, value interface{}) *types.Transacti // RollupForgeBatch is the interface to call the smart contract function func (c *Client) RollupForgeBatch(*eth.RollupForgeBatchArgs) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // CtlAddBatch adds forged batch to the Rollup, without checking any ZKProof func (c *Client) CtlAddBatch(args *eth.RollupForgeBatchArgs) { + c.rw.Lock() + defer c.rw.Unlock() + nextBlock := c.nextBlock() r := nextBlock.Rollup r.State.StateRoot = args.NewStRoot @@ -352,6 +476,9 @@ func (c *Client) CtlAddBatch(args *eth.RollupForgeBatchArgs) { // RollupAddToken is the interface to call the smart contract function func (c *Client) RollupAddToken(tokenAddress ethCommon.Address) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + nextBlock := c.nextBlock() r := nextBlock.Rollup if _, ok := r.State.TokenMap[tokenAddress]; ok { @@ -372,76 +499,121 @@ func (c *Client) RollupAddToken(tokenAddress ethCommon.Address) (*types.Transact // RollupWithdrawMerkleProof is the interface to call the smart contract function func (c *Client) RollupWithdrawMerkleProof(tokenID int64, balance *big.Int, babyPubKey *babyjub.PublicKey, numExitRoot int64, siblings []*big.Int, idx int64, instantWithdraw bool) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupForceExit is the interface to call the smart contract function func (c *Client) RollupForceExit(fromIdx int64, amountF utils.Float16, tokenID int64) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupForceTransfer is the interface to call the smart contract function func (c *Client) RollupForceTransfer(fromIdx int64, amountF utils.Float16, tokenID, toIdx int64) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupCreateAccountDepositTransfer is the interface to call the smart contract function func (c *Client) RollupCreateAccountDepositTransfer(babyPubKey babyjub.PublicKey, loadAmountF, amountF utils.Float16, tokenID int64, toIdx int64) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupDepositTransfer is the interface to call the smart contract function func (c *Client) RollupDepositTransfer(fromIdx int64, loadAmountF, amountF utils.Float16, tokenID int64, toIdx int64) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupDeposit is the interface to call the smart contract function func (c *Client) RollupDeposit(fromIdx int64, loadAmountF utils.Float16, tokenID int64) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupCreateAccountDepositFromRelayer is the interface to call the smart contract function func (c *Client) RollupCreateAccountDepositFromRelayer(accountCreationAuthSig []byte, babyPubKey babyjub.PublicKey, loadAmountF utils.Float16) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupCreateAccountDeposit is the interface to call the smart contract function func (c *Client) RollupCreateAccountDeposit(babyPubKey babyjub.PublicKey, loadAmountF utils.Float16, tokenID int64) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupGetTokenAddress is the interface to call the smart contract function func (c *Client) RollupGetTokenAddress(tokenID int64) (*ethCommon.Address, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return nil, errTODO } // RollupGetL1TxFromQueue is the interface to call the smart contract function func (c *Client) RollupGetL1TxFromQueue(queue int64, position int64) ([]byte, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return nil, errTODO } // RollupGetQueue is the interface to call the smart contract function func (c *Client) RollupGetQueue(queue int64) ([]byte, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return nil, errTODO } // RollupUpdateForgeL1Timeout is the interface to call the smart contract function func (c *Client) RollupUpdateForgeL1Timeout(newForgeL1Timeout int64) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupUpdateFeeL1UserTx is the interface to call the smart contract function func (c *Client) RollupUpdateFeeL1UserTx(newFeeL1UserTx *big.Int) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupUpdateFeeAddToken is the interface to call the smart contract function func (c *Client) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // RollupUpdateTokensHEZ is the interface to call the smart contract function func (c *Client) RollupUpdateTokensHEZ(newTokenHEZ ethCommon.Address) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } @@ -452,11 +624,17 @@ func (c *Client) RollupUpdateTokensHEZ(newTokenHEZ ethCommon.Address) (*types.Tr // RollupConstants returns the Constants of the Rollup Smart Contract func (c *Client) RollupConstants() (*eth.RollupConstants, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return nil, errTODO } // RollupEventsByBlock returns the events in a block that happened in the Rollup Smart Contract func (c *Client) RollupEventsByBlock(blockNum int64) (*eth.RollupEvents, *ethCommon.Hash, error) { + c.rw.RLock() + defer c.rw.RUnlock() + block, ok := c.blocks[blockNum] if !ok { return nil, nil, fmt.Errorf("Block %v doesn't exist", blockNum) @@ -466,6 +644,9 @@ func (c *Client) RollupEventsByBlock(blockNum int64) (*eth.RollupEvents, *ethCom // RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the Rollup Smart Contract in the given transaction func (c *Client) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*eth.RollupForgeBatchArgs, error) { + c.rw.RLock() + defer c.rw.RUnlock() + forgeBatchArgs, ok := c.forgeBatchArgs[ethTxHash] if !ok { return nil, fmt.Errorf("transaction not found") @@ -479,104 +660,194 @@ func (c *Client) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*eth.RollupForg // AuctionSetSlotDeadline is the interface to call the smart contract function func (c *Client) AuctionSetSlotDeadline(newDeadline uint8) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetSlotDeadline is the interface to call the smart contract function func (c *Client) AuctionGetSlotDeadline() (uint8, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return 0, errTODO } // AuctionSetOpenAuctionSlots is the interface to call the smart contract function func (c *Client) AuctionSetOpenAuctionSlots(newOpenAuctionSlots uint16) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetOpenAuctionSlots is the interface to call the smart contract function -func (c *Client) AuctionGetOpenAuctionSlots() (uint16, error) { return 0, errTODO } +func (c *Client) AuctionGetOpenAuctionSlots() (uint16, error) { + c.rw.RLock() + defer c.rw.RUnlock() + + return 0, errTODO +} // AuctionSetClosedAuctionSlots is the interface to call the smart contract function func (c *Client) AuctionSetClosedAuctionSlots(newClosedAuctionSlots uint16) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetClosedAuctionSlots is the interface to call the smart contract function func (c *Client) AuctionGetClosedAuctionSlots() (uint16, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return 0, errTODO } // AuctionSetOutbidding is the interface to call the smart contract function func (c *Client) AuctionSetOutbidding(newOutbidding uint8) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetOutbidding is the interface to call the smart contract function func (c *Client) AuctionGetOutbidding() (uint8, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return 0, errTODO } // AuctionSetAllocationRatio is the interface to call the smart contract function func (c *Client) AuctionSetAllocationRatio(newAllocationRatio [3]uint8) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetAllocationRatio is the interface to call the smart contract function func (c *Client) AuctionGetAllocationRatio() ([3]uint8, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return [3]uint8{}, errTODO } // AuctionSetDonationAddress is the interface to call the smart contract function func (c *Client) AuctionSetDonationAddress(newDonationAddress ethCommon.Address) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetDonationAddress is the interface to call the smart contract function func (c *Client) AuctionGetDonationAddress() (*ethCommon.Address, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return nil, errTODO } // AuctionSetBootCoordinator is the interface to call the smart contract function func (c *Client) AuctionSetBootCoordinator(newBootCoordinator ethCommon.Address) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetBootCoordinator is the interface to call the smart contract function func (c *Client) AuctionGetBootCoordinator() (*ethCommon.Address, error) { - return nil, errTODO + c.rw.RLock() + defer c.rw.RUnlock() + + currentBlock := c.currentBlock() + a := currentBlock.Auction + + return &a.Vars.BootCoordinator, nil } // AuctionChangeEpochMinBid is the interface to call the smart contract function func (c *Client) AuctionChangeEpochMinBid(slotEpoch int64, newInitialMinBid *big.Int) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionRegisterCoordinator is the interface to call the smart contract function func (c *Client) AuctionRegisterCoordinator(forgerAddress ethCommon.Address, URL string) (*types.Transaction, error) { - return nil, errTODO + c.rw.Lock() + defer c.rw.Unlock() + + nextBlock := c.nextBlock() + a := nextBlock.Auction + + if _, ok := a.State.Coordinators[forgerAddress]; ok { + return nil, fmt.Errorf("Already registered") + } + a.State.Coordinators[forgerAddress] = ð.Coordinator{ + WithdrawalAddress: c.addr, + URL: URL, + } + + a.Events.NewCoordinator = append(a.Events.NewCoordinator, + eth.AuctionEventNewCoordinator{ + ForgerAddress: forgerAddress, + WithdrawalAddress: c.addr, + URL: URL, + }) + + type data struct { + ForgerAddress ethCommon.Address + URL string + } + return c.newTransaction("registercoordinator", data{forgerAddress, URL}), nil } // AuctionIsRegisteredCoordinator is the interface to call the smart contract function func (c *Client) AuctionIsRegisteredCoordinator(forgerAddress ethCommon.Address) (bool, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return false, errTODO } // AuctionUpdateCoordinatorInfo is the interface to call the smart contract function func (c *Client) AuctionUpdateCoordinatorInfo(forgerAddress ethCommon.Address, newWithdrawAddress ethCommon.Address, newURL string) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionGetCurrentSlotNumber is the interface to call the smart contract function func (c *Client) AuctionGetCurrentSlotNumber() (int64, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return 0, errTODO } // AuctionGetMinBidBySlot is the interface to call the smart contract function func (c *Client) AuctionGetMinBidBySlot(slot int64) (*big.Int, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return nil, errTODO } // AuctionGetMinBidEpoch is the interface to call the smart contract function func (c *Client) AuctionGetMinBidEpoch(epoch uint8) (*big.Int, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return nil, errTODO } @@ -587,16 +858,64 @@ func (c *Client) AuctionGetMinBidEpoch(epoch uint8) (*big.Int, error) { // AuctionBid is the interface to call the smart contract function func (c *Client) AuctionBid(slot int64, bidAmount *big.Int, forger ethCommon.Address) (*types.Transaction, error) { - return nil, errTODO + c.rw.Lock() + defer c.rw.Unlock() + + nextBlock := c.nextBlock() + a := nextBlock.Auction + + if slot < a.getCurrentSlotNumber()+int64(a.Vars.ClosedAuctionSlots) { + return nil, errBidClosed + } + + if slot >= a.getCurrentSlotNumber()+int64(a.Vars.ClosedAuctionSlots)+int64(a.Vars.OpenAuctionSlots) { + return nil, errBidNotOpen + } + + minBid, err := a.getMinBidBySlot(slot) + if err != nil { + return nil, err + } + if bidAmount.Cmp(minBid) == -1 { + return nil, errBidBelowMin + } + + if _, ok := a.State.Coordinators[forger]; !ok { + return nil, errCoordNotReg + } + + slotState, ok := a.State.Slots[slot] + if !ok { + slotState = ð.SlotState{} + a.State.Slots[slot] = slotState + } + slotState.Forger = forger + slotState.BidAmount = bidAmount + + a.Events.NewBid = append(a.Events.NewBid, + eth.AuctionEventNewBid{Slot: slot, BidAmount: bidAmount, CoordinatorForger: forger}) + + type data struct { + Slot int64 + BidAmount *big.Int + Forger ethCommon.Address + } + return c.newTransaction("bid", data{slot, bidAmount, forger}), nil } // AuctionMultiBid is the interface to call the smart contract function func (c *Client) AuctionMultiBid(startingSlot int64, endingSlot int64, slotEpoch [6]bool, maxBid, closedMinBid, budget *big.Int, forger ethCommon.Address) (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionCanForge is the interface to call the smart contract function func (c *Client) AuctionCanForge(forger ethCommon.Address) (bool, error) { + c.rw.RLock() + defer c.rw.RUnlock() + return false, errTODO } @@ -607,15 +926,28 @@ func (c *Client) AuctionCanForge(forger ethCommon.Address) (bool, error) { // AuctionClaimHEZ is the interface to call the smart contract function func (c *Client) AuctionClaimHEZ() (*types.Transaction, error) { + c.rw.Lock() + defer c.rw.Unlock() + return nil, errTODO } // AuctionConstants returns the Constants of the Auction Smart Contract func (c *Client) AuctionConstants() (*eth.AuctionConstants, error) { - return nil, errTODO + c.rw.RLock() + defer c.rw.RUnlock() + + return c.auctionConstants, nil } // AuctionEventsByBlock returns the events in a block that happened in the Auction Smart Contract func (c *Client) AuctionEventsByBlock(blockNum int64) (*eth.AuctionEvents, *ethCommon.Hash, error) { - return nil, nil, errTODO + c.rw.RLock() + defer c.rw.RUnlock() + + block, ok := c.blocks[blockNum] + if !ok { + return nil, nil, fmt.Errorf("Block %v doesn't exist", blockNum) + } + return &block.Auction.Events, &block.Eth.Hash, nil } diff --git a/test/ethclient_test.go b/test/ethclient_test.go index 112f4a8..8826f63 100644 --- a/test/ethclient_test.go +++ b/test/ethclient_test.go @@ -17,42 +17,6 @@ import ( "github.com/stretchr/testify/require" ) -var clientSetup *ClientSetup - -func init() { - rollupConstants := ð.RollupConstants{} - rollupVariables := ð.RollupVariables{ - MaxTxVerifiers: make([]int, 0), - TokenHEZ: ethCommon.Address{}, - GovernanceAddress: ethCommon.Address{}, - SafetyBot: ethCommon.Address{}, - ConsensusContract: ethCommon.Address{}, - WithdrawalContract: ethCommon.Address{}, - FeeAddToken: big.NewInt(1), - ForgeL1Timeout: 16, - FeeL1UserTx: big.NewInt(2), - } - auctionConstants := ð.AuctionConstants{} - auctionVariables := ð.AuctionVariables{ - DonationAddress: ethCommon.Address{}, - BootCoordinator: ethCommon.Address{}, - MinBidEpoch: [6]*big.Int{ - big.NewInt(10), big.NewInt(11), big.NewInt(12), - big.NewInt(13), big.NewInt(14), big.NewInt(15)}, - ClosedAuctionSlots: 0, - OpenAuctionSlots: 0, - AllocationRatio: [3]uint8{}, - Outbidding: 0, - SlotDeadline: 0, - } - clientSetup = &ClientSetup{ - RollupConstants: rollupConstants, - RollupVariables: rollupVariables, - AuctionConstants: auctionConstants, - AuctionVariables: auctionVariables, - } -} - type timer struct { time int64 } @@ -66,16 +30,16 @@ func (t *timer) Time() int64 { func TestClientInterface(t *testing.T) { var c eth.ClientInterface var timer timer - client := NewClient(true, &timer, clientSetup) + clientSetup := NewClientSetupExample() + client := NewClient(true, &timer, ethCommon.Address{}, clientSetup) c = client require.NotNil(t, c) } -func TestEthClient(t *testing.T) { - token1Addr := ethCommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f") - +func TestClientEth(t *testing.T) { var timer timer - c := NewClient(true, &timer, clientSetup) + clientSetup := NewClientSetupExample() + c := NewClient(true, &timer, ethCommon.Address{}, clientSetup) blockNum, err := c.EthCurrentBlock() require.Nil(t, err) assert.Equal(t, int64(0), blockNum) @@ -98,6 +62,62 @@ func TestEthClient(t *testing.T) { require.Nil(t, err) assert.Equal(t, int64(2), block.EthBlockNum) assert.Equal(t, time.Unix(2, 0), block.Timestamp) +} + +func TestClientAuction(t *testing.T) { + addrWithdraw := ethCommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f") + addrForge := ethCommon.HexToAddress("0xCfAA413eEb796f328620a3630Ae39124cabcEa92") + addrForge2 := ethCommon.HexToAddress("0x1fCb4ac309428feCc61B1C8cA5823C15A5e1a800") + + var timer timer + clientSetup := NewClientSetupExample() + clientSetup.AuctionVariables.ClosedAuctionSlots = 2 + clientSetup.AuctionVariables.OpenAuctionSlots = 100 + c := NewClient(true, &timer, addrWithdraw, clientSetup) + + _, err := c.AuctionBid(0, big.NewInt(1), addrForge) + assert.Equal(t, errBidClosed, err) + + _, err = c.AuctionBid(102, big.NewInt(1), addrForge) + assert.Equal(t, errBidNotOpen, err) + + _, err = c.AuctionBid(101, big.NewInt(16), addrForge) + assert.Equal(t, errCoordNotReg, err) + + _, err = c.AuctionRegisterCoordinator(addrForge, "https://foo.bar") + assert.Nil(t, err) + + _, err = c.AuctionBid(3, big.NewInt(1), addrForge) + assert.Equal(t, errBidBelowMin, err) + + _, err = c.AuctionBid(3, big.NewInt(16), addrForge) + assert.Nil(t, err) + + _, err = c.AuctionRegisterCoordinator(addrForge2, "https://foo2.bar") + assert.Nil(t, err) + + _, err = c.AuctionBid(3, big.NewInt(16), addrForge2) + assert.Equal(t, errBidBelowMin, err) + + _, err = c.AuctionBid(3, big.NewInt(17), addrForge2) + assert.Nil(t, err) + + c.CtlMineBlock() + + blockNum, err := c.EthCurrentBlock() + require.Nil(t, err) + + auctionEvents, _, err := c.AuctionEventsByBlock(blockNum) + require.Nil(t, err) + assert.Equal(t, 2, len(auctionEvents.NewBid)) +} + +func TestClientRollup(t *testing.T) { + token1Addr := ethCommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f") + + var timer timer + clientSetup := NewClientSetupExample() + c := NewClient(true, &timer, ethCommon.Address{}, clientSetup) // Add a token @@ -123,7 +143,7 @@ func TestEthClient(t *testing.T) { } c.CtlMineBlock() - blockNum, err = c.EthCurrentBlock() + blockNum, err := c.EthCurrentBlock() require.Nil(t, err) rollupEvents, _, err := c.RollupEventsByBlock(blockNum) require.Nil(t, err)