hermez-node/test/ethclient.go
test/ethclient.goRaw
1package test
2
3import (
4 "context"
5 "encoding/binary"
6 "encoding/json"
7 "fmt"
8 "math/big"
9 "reflect"
10 "sync"
11 "time"
12
13 "github.com/ethereum/go-ethereum"
14 ethCommon "github.com/ethereum/go-ethereum/common"
15 "github.com/ethereum/go-ethereum/core/types"
16 "github.com/hermeznetwork/hermez-node/common"
17 "github.com/hermeznetwork/hermez-node/eth"
18 "github.com/hermeznetwork/hermez-node/log"
19 "github.com/iden3/go-iden3-crypto/babyjub"
20 "github.com/mitchellh/copystructure"
21)
22
23func init() {
24 copystructure.Copiers[reflect.TypeOf(big.Int{})] =
25 func(raw interface{}) (interface{}, error) {
26 in := raw.(big.Int)
27 out := new(big.Int).Set(&in)
28 return *out, nil
29 }
30}
31
32// RollupBlock stores all the data related to the Rollup SC from an ethereum block
33type RollupBlock struct {
34 State eth.RollupState
35 Vars common.RollupVariables
36 Events eth.RollupEvents
37 Txs map[ethCommon.Hash]*types.Transaction
38 Constants *common.RollupConstants
39 Eth *EthereumBlock
40}
41
42func (r *RollupBlock) addTransaction(tx *types.Transaction) *types.Transaction {
43 txHash := tx.Hash()
44 r.Txs[txHash] = tx
45 return tx
46}
47
48var (
49 errBidClosed = fmt.Errorf("Bid has already been closed")
50 errBidNotOpen = fmt.Errorf("Bid has not been opened yet")
51 errBidBelowMin = fmt.Errorf("Bid below minimum")
52 errCoordNotReg = fmt.Errorf("Coordinator not registered")
53)
54
55// AuctionBlock stores all the data related to the Auction SC from an ethereum block
56type AuctionBlock struct {
57 State eth.AuctionState
58 Vars common.AuctionVariables
59 Events eth.AuctionEvents
60 Txs map[ethCommon.Hash]*types.Transaction
61 Constants *common.AuctionConstants
62 Eth *EthereumBlock
63}
64
65func (a *AuctionBlock) addTransaction(tx *types.Transaction) *types.Transaction {
66 txHash := tx.Hash()
67 a.Txs[txHash] = tx
68 return tx
69}
70
71func (a *AuctionBlock) getSlotNumber(blockNumber int64) int64 {
72 if a.Eth.BlockNum >= a.Constants.GenesisBlockNum {
73 return (blockNumber - a.Constants.GenesisBlockNum) / int64(a.Constants.BlocksPerSlot)
74 }
75 return 0
76}
77
78func (a *AuctionBlock) getCurrentSlotNumber() int64 {
79 return a.getSlotNumber(a.Eth.BlockNum)
80}
81
82func (a *AuctionBlock) getSlotSet(slot int64) int64 {
83 return slot % int64(len(a.Vars.DefaultSlotSetBid))
84}
85
86func (a *AuctionBlock) getMinBidBySlot(slot int64) (*big.Int, error) {
87 if slot < a.getCurrentSlotNumber()+int64(a.Vars.ClosedAuctionSlots) {
88 return nil, errBidClosed
89 }
90
91 slotSet := a.getSlotSet(slot)
92 // fmt.Println("slot:", slot, "slotSet:", slotSet)
93 var prevBid *big.Int
94 slotState, ok := a.State.Slots[slot]
95 if !ok {
96 slotState = eth.NewSlotState()
97 a.State.Slots[slot] = slotState
98 }
99 // If the bidAmount for a slot is 0 it means that it has not yet been bid, so the midBid will be the minimum
100 // bid for the slot time plus the outbidding set, otherwise it will be the bidAmount plus the outbidding
101 if slotState.BidAmount.Cmp(big.NewInt(0)) == 0 {
102 prevBid = a.Vars.DefaultSlotSetBid[slotSet]
103 } else {
104 prevBid = slotState.BidAmount
105 }
106 outBid := new(big.Int).Set(prevBid)
107 // fmt.Println("outBid:", outBid)
108 outBid.Mul(outBid, big.NewInt(int64(a.Vars.Outbidding)))
109 outBid.Div(outBid, big.NewInt(10000)) //nolint:gomnd
110 outBid.Add(prevBid, outBid)
111 // fmt.Println("minBid:", outBid)
112 return outBid, nil
113}
114
115func (a *AuctionBlock) forge(forger ethCommon.Address) error {
116 if ok, err := a.canForge(forger, a.Eth.BlockNum); err != nil {
117 return err
118 } else if !ok {
119 return fmt.Errorf("Can't forge")
120 }
121
122 slotToForge := a.getSlotNumber(a.Eth.BlockNum)
123 slotState, ok := a.State.Slots[slotToForge]
124 if !ok {
125 slotState = eth.NewSlotState()
126 a.State.Slots[slotToForge] = slotState
127 }
128 slotState.Fulfilled = true
129
130 a.Events.NewForge = append(a.Events.NewForge, eth.AuctionEventNewForge{
131 Forger: forger,
132 SlotToForge: slotToForge,
133 })
134 return nil
135}
136
137func (a *AuctionBlock) canForge(forger ethCommon.Address, blockNum int64) (bool, error) {
138 if blockNum < a.Constants.GenesisBlockNum {
139 return false, fmt.Errorf("Auction has not started yet")
140 }
141
142 slotToForge := a.getSlotNumber(blockNum)
143 // Get the relativeBlock to check if the slotDeadline has been exceeded
144 relativeBlock := blockNum - (a.Constants.GenesisBlockNum + (slotToForge * int64(a.Constants.BlocksPerSlot)))
145
146 // If the closedMinBid is 0 it means that we have to take as minBid the one that is set for this slot set,
147 // otherwise the one that has been saved will be used
148 var minBid *big.Int
149 slotState, ok := a.State.Slots[slotToForge]
150 if !ok {
151 slotState = eth.NewSlotState()
152 a.State.Slots[slotToForge] = slotState
153 }
154 if slotState.ClosedMinBid.Cmp(big.NewInt(0)) == 0 {
155 minBid = a.Vars.DefaultSlotSetBid[a.getSlotSet(slotToForge)]
156 } else {
157 minBid = slotState.ClosedMinBid
158 }
159
160 if !slotState.Fulfilled && (relativeBlock >= int64(a.Vars.SlotDeadline)) {
161 // if the relative block has exceeded the slotDeadline and no batch has been forged, anyone can forge
162 return true, nil
163 // TODO, find the forger set by the Bidder
164 } else if coord, ok := a.State.Coordinators[slotState.Bidder]; ok &&
165 coord.Forger == forger && slotState.BidAmount.Cmp(minBid) >= 0 {
166 // if forger bidAmount has exceeded the minBid it can forge
167 return true, nil
168 } else if a.Vars.BootCoordinator == forger && slotState.BidAmount.Cmp(minBid) == -1 {
169 // if it's the boot coordinator and it has not been bid or the bid is below the minimum it can forge
170 return true, nil
171 } else {
172 return false, nil
173 }
174}
175
176// EthereumBlock stores all the generic data related to the an ethereum block
177type EthereumBlock struct {
178 BlockNum int64
179 Time int64
180 Hash ethCommon.Hash
181 ParentHash ethCommon.Hash
182 Tokens map[ethCommon.Address]eth.ERC20Consts
183 // state ethState
184}
185
186// Block represents a ethereum block
187type Block struct {
188 Rollup *RollupBlock
189 Auction *AuctionBlock
190 Eth *EthereumBlock
191}
192
193func (b *Block) copy() *Block {
194 bCopyRaw, err := copystructure.Copy(b)
195 if err != nil {
196 panic(err)
197 }
198 bCopy := bCopyRaw.(*Block)
199 return bCopy
200}
201
202// Next prepares the successive block.
203func (b *Block) Next() *Block {
204 blockNext := b.copy()
205 blockNext.Rollup.Events = eth.NewRollupEvents()
206 blockNext.Auction.Events = eth.NewAuctionEvents()
207
208 blockNext.Eth.BlockNum = b.Eth.BlockNum + 1
209 blockNext.Eth.ParentHash = b.Eth.Hash
210
211 blockNext.Rollup.Constants = b.Rollup.Constants
212 blockNext.Auction.Constants = b.Auction.Constants
213 blockNext.Rollup.Eth = blockNext.Eth
214 blockNext.Auction.Eth = blockNext.Eth
215
216 return blockNext
217}
218
219// ClientSetup is used to initialize the constants of the Smart Contracts and
220// other details of the test Client
221type ClientSetup struct {
222 RollupConstants *common.RollupConstants
223 RollupVariables *common.RollupVariables
224 AuctionConstants *common.AuctionConstants
225 AuctionVariables *common.AuctionVariables
226 VerifyProof bool
227}
228
229// NewClientSetupExample returns a ClientSetup example with hardcoded realistic
230// values. With this setup, the rollup genesis will be block 1, and block 0
231// and 1 will be premined.
232//nolint:gomnd
233func NewClientSetupExample() *ClientSetup {
234 // rfield, ok := new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)
235 // if !ok {
236 // panic("bad rfield")
237 // }
238 initialMinimalBidding, ok := new(big.Int).SetString("10000000000000000000", 10) // 10 * (1e18)
239 if !ok {
240 panic("bad initialMinimalBidding")
241 }
242 tokenHEZ := ethCommon.HexToAddress("0x51D243D62852Bba334DD5cc33f242BAc8c698074")
243 governanceAddress := ethCommon.HexToAddress("0x688EfD95BA4391f93717CF02A9aED9DBD2855cDd")
244 rollupConstants := &common.RollupConstants{
245 Verifiers: []common.RollupVerifierStruct{
246 {
247 MaxTx: 2048,
248 NLevels: 32,
249 },
250 },
251 TokenHEZ: tokenHEZ,
252 HermezGovernanceDAOAddress: governanceAddress,
253 SafetyAddress: ethCommon.HexToAddress("0x84d8B79E84fe87B14ad61A554e740f6736bF4c20"),
254 HermezAuctionContract: ethCommon.HexToAddress("0x8E442975805fb1908f43050c9C1A522cB0e28D7b"),
255 WithdrawDelayerContract: ethCommon.HexToAddress("0x5CB7979cBdbf65719BEE92e4D15b7b7Ed3D79114"),
256 }
257 rollupVariables := &common.RollupVariables{
258 FeeAddToken: big.NewInt(11),
259 ForgeL1L2BatchTimeout: 9,
260 WithdrawalDelay: 80,
261 }
262 auctionConstants := &common.AuctionConstants{
263 BlocksPerSlot: 40,
264 InitialMinimalBidding: initialMinimalBidding,
265 GenesisBlockNum: 1,
266 GovernanceAddress: governanceAddress,
267 TokenHEZ: tokenHEZ,
268 HermezRollup: ethCommon.HexToAddress("0x474B6e29852257491cf283EfB1A9C61eBFe48369"),
269 }
270 auctionVariables := &common.AuctionVariables{
271 DonationAddress: ethCommon.HexToAddress("0x61Ed87CF0A1496b49A420DA6D84B58196b98f2e7"),
272 BootCoordinator: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
273 DefaultSlotSetBid: [6]*big.Int{
274 big.NewInt(1000), big.NewInt(1100), big.NewInt(1200),
275 big.NewInt(1300), big.NewInt(1400), big.NewInt(1500)},
276 ClosedAuctionSlots: 2,
277 OpenAuctionSlots: 4320,
278 AllocationRatio: [3]uint16{4000, 4000, 2000},
279 Outbidding: 1000,
280 SlotDeadline: 20,
281 }
282 return &ClientSetup{
283 RollupConstants: rollupConstants,
284 RollupVariables: rollupVariables,
285 AuctionConstants: auctionConstants,
286 AuctionVariables: auctionVariables,
287 }
288}
289
290// Timer is an interface to simulate a source of time, useful to advance time
291// virtually.
292type Timer interface {
293 Time() int64
294}
295
296// type forgeBatchArgs struct {
297// ethTx *types.Transaction
298// blockNum int64
299// blockHash ethCommon.Hash
300// }
301
302type batch struct {
303 ForgeBatchArgs eth.RollupForgeBatchArgs
304 Sender ethCommon.Address
305}
306
307// Client implements the eth.ClientInterface interface, allowing to manipulate the
308// values for testing, working with deterministic results.
309type Client struct {
310 rw *sync.RWMutex
311 log bool
312 addr *ethCommon.Address
313 rollupConstants *common.RollupConstants
314 auctionConstants *common.AuctionConstants
315 blocks map[int64]*Block
316 // state state
317 blockNum int64 // last mined block num
318 maxBlockNum int64 // highest block num calculated
319 timer Timer
320 hasher hasher
321
322 forgeBatchArgsPending map[ethCommon.Hash]*batch
323 forgeBatchArgs map[ethCommon.Hash]*batch
324}
325
326// NewClient returns a new test Client that implements the eth.IClient
327// interface, at the given initialBlockNumber.
328func NewClient(l bool, timer Timer, addr *ethCommon.Address, setup *ClientSetup) *Client {
329 blocks := make(map[int64]*Block)
330 blockNum := int64(0)
331
332 hasher := hasher{}
333 // Add ethereum genesis block
334 mapL1TxQueue := make(map[int64]*eth.QueueStruct)
335 mapL1TxQueue[0] = eth.NewQueueStruct()
336 mapL1TxQueue[1] = eth.NewQueueStruct()
337 blockCurrent := &Block{
338 Rollup: &RollupBlock{
339 State: eth.RollupState{
340 StateRoot: big.NewInt(0),
341 ExitRoots: make([]*big.Int, 0),
342 ExitNullifierMap: make(map[[256 / 8]byte]bool),
343 // TokenID = 0 is ETH. Set first entry in TokenList with 0x0 address for ETH.
344 TokenList: []ethCommon.Address{{}},
345 TokenMap: make(map[ethCommon.Address]bool),
346 MapL1TxQueue: mapL1TxQueue,
347 LastL1L2Batch: 0,
348 CurrentToForgeL1TxsNum: 0,
349 LastToForgeL1TxsNum: 1,
350 CurrentIdx: 0,
351 },
352 Vars: *setup.RollupVariables,
353 Txs: make(map[ethCommon.Hash]*types.Transaction),
354 Events: eth.NewRollupEvents(),
355 Constants: setup.RollupConstants,
356 },
357 Auction: &AuctionBlock{
358 State: eth.AuctionState{
359 Slots: make(map[int64]*eth.SlotState),
360 PendingBalances: make(map[ethCommon.Address]*big.Int),
361 Coordinators: make(map[ethCommon.Address]*eth.Coordinator),
362 },
363 Vars: *setup.AuctionVariables,
364 Txs: make(map[ethCommon.Hash]*types.Transaction),
365 Events: eth.NewAuctionEvents(),
366 Constants: setup.AuctionConstants,
367 },
368 Eth: &EthereumBlock{
369 BlockNum: blockNum,
370 Time: timer.Time(),
371 Hash: hasher.Next(),
372 ParentHash: ethCommon.Hash{},
373 Tokens: make(map[ethCommon.Address]eth.ERC20Consts),
374 },
375 }
376 blockCurrent.Rollup.Eth = blockCurrent.Eth
377 blockCurrent.Auction.Eth = blockCurrent.Eth
378 blocks[blockNum] = blockCurrent
379 blockNext := blockCurrent.Next()
380 blocks[blockNum+1] = blockNext
381
382 c := Client{
383 rw: &sync.RWMutex{},
384 log: l,
385 addr: addr,
386 rollupConstants: setup.RollupConstants,
387 auctionConstants: setup.AuctionConstants,
388 blocks: blocks,
389 timer: timer,
390 hasher: hasher,
391 forgeBatchArgsPending: make(map[ethCommon.Hash]*batch),
392 forgeBatchArgs: make(map[ethCommon.Hash]*batch),
393 blockNum: blockNum,
394 maxBlockNum: blockNum,
395 }
396
397 for i := int64(1); i < setup.AuctionConstants.GenesisBlockNum+1; i++ {
398 c.CtlMineBlock()
399 }
400
401 return &c
402}
403
404//
405// Mock Control
406//
407
408func (c *Client) setNextBlock(block *Block) {
409 c.blocks[c.blockNum+1] = block
410}
411
412func (c *Client) revertIfErr(err error, block *Block) {
413 if err != nil {
414 log.Infow("TestClient revert", "block", block.Eth.BlockNum, "err", err)
415 c.setNextBlock(block)
416 }
417}
418
419// Debugf calls log.Debugf if c.log is true
420func (c *Client) Debugf(template string, args ...interface{}) {
421 if c.log {
422 log.Debugf(template, args...)
423 }
424}
425
426// Debugw calls log.Debugw if c.log is true
427func (c *Client) Debugw(template string, kv ...interface{}) {
428 if c.log {
429 log.Debugw(template, kv...)
430 }
431}
432
433type hasher struct {
434 counter uint64
435}
436
437// Next returns the next hash
438func (h *hasher) Next() ethCommon.Hash {
439 var hash ethCommon.Hash
440 binary.LittleEndian.PutUint64(hash[:], h.counter)
441 h.counter++
442 return hash
443}
444
445func (c *Client) nextBlock() *Block {
446 return c.blocks[c.blockNum+1]
447}
448
449func (c *Client) currentBlock() *Block {
450 return c.blocks[c.blockNum]
451}
452
453// CtlSetAddr sets the address of the client
454func (c *Client) CtlSetAddr(addr ethCommon.Address) {
455 c.addr = &addr
456}
457
458// CtlMineBlock moves one block forward
459func (c *Client) CtlMineBlock() {
460 c.rw.Lock()
461 defer c.rw.Unlock()
462
463 blockCurrent := c.nextBlock()
464 c.blockNum++
465 c.maxBlockNum = c.blockNum
466 blockCurrent.Eth.Time = c.timer.Time()
467 blockCurrent.Eth.Hash = c.hasher.Next()
468 for ethTxHash, forgeBatchArgs := range c.forgeBatchArgsPending {
469 c.forgeBatchArgs[ethTxHash] = forgeBatchArgs
470 }
471 c.forgeBatchArgsPending = make(map[ethCommon.Hash]*batch)
472
473 blockNext := blockCurrent.Next()
474 c.blocks[c.blockNum+1] = blockNext
475 c.Debugw("TestClient mined block", "blockNum", c.blockNum)
476}
477
478// CtlRollback discards the last mined block. Use this to replace a mined
479// block to simulate reorgs.
480func (c *Client) CtlRollback() {
481 c.rw.Lock()
482 defer c.rw.Unlock()
483
484 if c.blockNum == 0 {
485 panic("Can't rollback at blockNum = 0")
486 }
487 delete(c.blocks, c.blockNum+1) // delete next block
488 delete(c.blocks, c.blockNum) // delete current block
489 c.blockNum--
490 blockCurrent := c.blocks[c.blockNum]
491 blockNext := blockCurrent.Next()
492 c.blocks[c.blockNum+1] = blockNext
493}
494
495//
496// Ethereum
497//
498
499// EthCurrentBlock returns the current blockNum
500func (c *Client) EthCurrentBlock() (int64, error) {
501 c.rw.RLock()
502 defer c.rw.RUnlock()
503
504 if c.blockNum < c.maxBlockNum {
505 panic("blockNum has decreased. " +
506 "After a rollback you must mine to reach the same or higher blockNum")
507 }
508 return c.blockNum, nil
509}
510
511// EthTransactionReceipt returns the transaction receipt of the given txHash
512func (c *Client) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) {
513 c.rw.RLock()
514 defer c.rw.RUnlock()
515
516 for i := int64(0); i < c.blockNum; i++ {
517 b := c.blocks[i]
518 _, ok := b.Rollup.Txs[txHash]
519 if !ok {
520 _, ok = b.Auction.Txs[txHash]
521 }
522 if ok {
523 return &types.Receipt{
524 TxHash: txHash,
525 Status: types.ReceiptStatusSuccessful,
526 BlockHash: b.Eth.Hash,
527 BlockNumber: big.NewInt(b.Eth.BlockNum),
528 }, nil
529 }
530 }
531
532 return nil, nil
533}
534
535// CtlAddERC20 adds an ERC20 token to the blockchain.
536func (c *Client) CtlAddERC20(tokenAddr ethCommon.Address, constants eth.ERC20Consts) {
537 nextBlock := c.nextBlock()
538 e := nextBlock.Eth
539 e.Tokens[tokenAddr] = constants
540}
541
542// EthERC20Consts returns the constants defined for a particular ERC20 Token instance.
543func (c *Client) EthERC20Consts(tokenAddr ethCommon.Address) (*eth.ERC20Consts, error) {
544 currentBlock := c.currentBlock()
545 e := currentBlock.Eth
546 if constants, ok := e.Tokens[tokenAddr]; ok {
547 return &constants, nil
548 }
549 return nil, fmt.Errorf("tokenAddr not found")
550}
551
552// func newHeader(number *big.Int) *types.Header {
553// return &types.Header{
554// Number: number,
555// Time: uint64(number.Int64()),
556// }
557// }
558
559// EthHeaderByNumber returns the *types.Header for the given block number in a
560// deterministic way.
561// func (c *Client) EthHeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
562// return newHeader(number), nil
563// }
564
565// EthBlockByNumber returns the *common.Block for the given block number in a
566// deterministic way.
567func (c *Client) EthBlockByNumber(ctx context.Context, blockNum int64) (*common.Block, error) {
568 c.rw.RLock()
569 defer c.rw.RUnlock()
570
571 if blockNum > c.blockNum {
572 return nil, ethereum.NotFound
573 }
574 block := c.blocks[blockNum]
575 return &common.Block{
576 EthBlockNum: blockNum,
577 Timestamp: time.Unix(block.Eth.Time, 0),
578 Hash: block.Eth.Hash,
579 ParentHash: block.Eth.ParentHash,
580 }, nil
581}
582
583// EthAddress returns the ethereum address of the account loaded into the Client
584func (c *Client) EthAddress() (*ethCommon.Address, error) {
585 if c.addr == nil {
586 return nil, eth.ErrAccountNil
587 }
588 return c.addr, nil
589}
590
591var errTODO = fmt.Errorf("TODO: Not implemented yet")
592
593//
594// Rollup
595//
596
597// CtlAddL1TxUser adds an L1TxUser to the L1UserTxs queue of the Rollup
598// func (c *Client) CtlAddL1TxUser(l1Tx *common.L1Tx) {
599// c.rw.Lock()
600// defer c.rw.Unlock()
601//
602// nextBlock := c.nextBlock()
603// r := nextBlock.Rollup
604// queue := r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum]
605// if len(queue.L1TxQueue) >= eth.RollupConstMaxL1UserTx {
606// r.State.LastToForgeL1TxsNum++
607// r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] = eth.NewQueueStruct()
608// queue = r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum]
609// }
610// if int64(l1Tx.FromIdx) > r.State.CurrentIdx {
611// panic("l1Tx.FromIdx > r.State.CurrentIdx")
612// }
613// if int(l1Tx.TokenID)+1 > len(r.State.TokenList) {
614// panic("l1Tx.TokenID + 1 > len(r.State.TokenList)")
615// }
616// queue.L1TxQueue = append(queue.L1TxQueue, *l1Tx)
617// r.Events.L1UserTx = append(r.Events.L1UserTx, eth.RollupEventL1UserTx{
618// L1Tx: *l1Tx,
619// ToForgeL1TxsNum: r.State.LastToForgeL1TxsNum,
620// Position: len(queue.L1TxQueue) - 1,
621// })
622// }
623
624// RollupL1UserTxERC20Permit is the interface to call the smart contract function
625func (c *Client) RollupL1UserTxERC20Permit(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64, deadline *big.Int) (tx *types.Transaction, err error) {
626 log.Error("TODO")
627 return nil, errTODO
628}
629
630// RollupL1UserTxERC20ETH sends an L1UserTx to the Rollup.
631func (c *Client) RollupL1UserTxERC20ETH(
632 fromBJJ *babyjub.PublicKey,
633 fromIdx int64,
634 loadAmount *big.Int,
635 amount *big.Int,
636 tokenID uint32,
637 toIdx int64,
638) (tx *types.Transaction, err error) {
639 c.rw.Lock()
640 defer c.rw.Unlock()
641 cpy := c.nextBlock().copy()
642 defer func() { c.revertIfErr(err, cpy) }()
643
644 _, err = common.NewFloat16(amount)
645 if err != nil {
646 return nil, err
647 }
648 _, err = common.NewFloat16(loadAmount)
649 if err != nil {
650 return nil, err
651 }
652
653 nextBlock := c.nextBlock()
654 r := nextBlock.Rollup
655 queue := r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum]
656 if len(queue.L1TxQueue) >= common.RollupConstMaxL1UserTx {
657 r.State.LastToForgeL1TxsNum++
658 r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] = eth.NewQueueStruct()
659 queue = r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum]
660 }
661 if fromIdx > r.State.CurrentIdx {
662 panic("l1Tx.FromIdx > r.State.CurrentIdx")
663 }
664 if int(tokenID)+1 > len(r.State.TokenList) {
665 panic("l1Tx.TokenID + 1 > len(r.State.TokenList)")
666 }
667 toForgeL1TxsNum := r.State.LastToForgeL1TxsNum
668 l1Tx, err := common.NewL1Tx(&common.L1Tx{
669 FromIdx: common.Idx(fromIdx),
670 FromEthAddr: *c.addr,
671 FromBJJ: fromBJJ,
672 Amount: amount,
673 LoadAmount: loadAmount,
674 TokenID: common.TokenID(tokenID),
675 ToIdx: common.Idx(toIdx),
676 ToForgeL1TxsNum: &toForgeL1TxsNum,
677 Position: len(queue.L1TxQueue),
678 UserOrigin: true,
679 })
680 if err != nil {
681 return nil, err
682 }
683
684 queue.L1TxQueue = append(queue.L1TxQueue, *l1Tx)
685 r.Events.L1UserTx = append(r.Events.L1UserTx, eth.RollupEventL1UserTx{
686 L1UserTx: *l1Tx,
687 })
688 return r.addTransaction(newTransaction("l1UserTxERC20ETH", l1Tx)), nil
689}
690
691// RollupL1UserTxERC777 is the interface to call the smart contract function
692// func (c *Client) RollupL1UserTxERC777(fromBJJ *babyjub.PublicKey, fromIdx int64, loadAmount *big.Int, amount *big.Int, tokenID uint32, toIdx int64) (*types.Transaction, error) {
693// log.Error("TODO")
694// return nil, errTODO
695// }
696
697// RollupRegisterTokensCount is the interface to call the smart contract function
698func (c *Client) RollupRegisterTokensCount() (*big.Int, error) {
699 log.Error("TODO")
700 return nil, errTODO
701}
702
703// RollupWithdrawCircuit is the interface to call the smart contract function
704func (c *Client) RollupWithdrawCircuit(proofA, proofC [2]*big.Int, proofB [2][2]*big.Int, tokenID uint32, numExitRoot, idx int64, amount *big.Int, instantWithdraw bool) (*types.Transaction, error) {
705 log.Error("TODO")
706 return nil, errTODO
707}
708
709// RollupWithdrawMerkleProof is the interface to call the smart contract function
710func (c *Client) RollupWithdrawMerkleProof(babyPubKey *babyjub.PublicKey, tokenID uint32, numExitRoot, idx int64, amount *big.Int, siblings []*big.Int, instantWithdraw bool) (*types.Transaction, error) {
711 log.Error("TODO")
712 return nil, errTODO
713}
714
715type transactionData struct {
716 Name string
717 Value interface{}
718}
719
720func newTransaction(name string, value interface{}) *types.Transaction {
721 data, err := json.Marshal(transactionData{name, value})
722 if err != nil {
723 panic(err)
724 }
725 return types.NewTransaction(0, ethCommon.Address{}, nil, 0, nil,
726 data)
727}
728
729// RollupForgeBatch is the interface to call the smart contract function
730func (c *Client) RollupForgeBatch(args *eth.RollupForgeBatchArgs) (tx *types.Transaction, err error) {
731 c.rw.Lock()
732 defer c.rw.Unlock()
733 cpy := c.nextBlock().copy()
734 defer func() { c.revertIfErr(err, cpy) }()
735 if c.addr == nil {
736 return nil, eth.ErrAccountNil
737 }
738
739 a := c.nextBlock().Auction
740 ok, err := a.canForge(*c.addr, a.Eth.BlockNum)
741 if err != nil {
742 return nil, err
743 }
744 if !ok {
745 return nil, fmt.Errorf("incorrect slot")
746 }
747
748 // TODO: Verify proof
749
750 // Auction
751 err = a.forge(*c.addr)
752 if err != nil {
753 return nil, err
754 }
755
756 // TODO: If successful, store the tx in a successful array.
757 // TODO: If failed, store the tx in a failed array.
758 // TODO: Add method to move the tx to another block, reapply it there, and possibly go from successful to failed.
759
760 return c.addBatch(args)
761}
762
763// CtlAddBatch adds forged batch to the Rollup, without checking any ZKProof
764func (c *Client) CtlAddBatch(args *eth.RollupForgeBatchArgs) {
765 c.rw.Lock()
766 defer c.rw.Unlock()
767
768 if _, err := c.addBatch(args); err != nil {
769 panic(err)
770 }
771}
772
773func (c *Client) addBatch(args *eth.RollupForgeBatchArgs) (*types.Transaction, error) {
774 nextBlock := c.nextBlock()
775 r := nextBlock.Rollup
776 r.State.StateRoot = args.NewStRoot
777 if args.NewLastIdx < r.State.CurrentIdx {
778 return nil, fmt.Errorf("args.NewLastIdx < r.State.CurrentIdx")
779 }
780 r.State.CurrentIdx = args.NewLastIdx
781 r.State.ExitRoots = append(r.State.ExitRoots, args.NewExitRoot)
782 if args.L1Batch {
783 r.State.CurrentToForgeL1TxsNum++
784 if r.State.CurrentToForgeL1TxsNum == r.State.LastToForgeL1TxsNum {
785 r.State.LastToForgeL1TxsNum++
786 r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] = eth.NewQueueStruct()
787 }
788 }
789 ethTx := r.addTransaction(newTransaction("forgebatch", args))
790 c.forgeBatchArgsPending[ethTx.Hash()] = &batch{*args, *c.addr}
791 r.Events.ForgeBatch = append(r.Events.ForgeBatch, eth.RollupEventForgeBatch{
792 BatchNum: int64(len(r.State.ExitRoots)),
793 EthTxHash: ethTx.Hash(),
794 })
795
796 return ethTx, nil
797}
798
799// RollupAddTokenSimple is a wrapper around RollupAddToken that automatically
800// sets `deadlie`.
801func (c *Client) RollupAddTokenSimple(tokenAddress ethCommon.Address, feeAddToken *big.Int) (tx *types.Transaction, err error) {
802 return c.RollupAddToken(tokenAddress, feeAddToken, big.NewInt(9999)) //nolint:gomnd
803}
804
805// RollupAddToken is the interface to call the smart contract function
806func (c *Client) RollupAddToken(tokenAddress ethCommon.Address, feeAddToken *big.Int,
807 deadline *big.Int) (tx *types.Transaction, err error) {
808 c.rw.Lock()
809 defer c.rw.Unlock()
810 cpy := c.nextBlock().copy()
811 defer func() { c.revertIfErr(err, cpy) }()
812 if c.addr == nil {
813 return nil, eth.ErrAccountNil
814 }
815
816 nextBlock := c.nextBlock()
817 r := nextBlock.Rollup
818 if _, ok := r.State.TokenMap[tokenAddress]; ok {
819 return nil, fmt.Errorf("Token %v already registered", tokenAddress)
820 }
821 if feeAddToken.Cmp(r.Vars.FeeAddToken) != 0 {
822 return nil, fmt.Errorf("Expected fee: %v but got: %v", r.Vars.FeeAddToken, feeAddToken)
823 }
824
825 r.State.TokenMap[tokenAddress] = true
826 r.State.TokenList = append(r.State.TokenList, tokenAddress)
827 r.Events.AddToken = append(r.Events.AddToken, eth.RollupEventAddToken{TokenAddress: tokenAddress,
828 TokenID: uint32(len(r.State.TokenList) - 1)})
829 return r.addTransaction(newTransaction("addtoken", tokenAddress)), nil
830}
831
832// RollupGetCurrentTokens is the interface to call the smart contract function
833func (c *Client) RollupGetCurrentTokens() (*big.Int, error) {
834 c.rw.RLock()
835 defer c.rw.RUnlock()
836
837 log.Error("TODO")
838 return nil, errTODO
839}
840
841// RollupUpdateForgeL1L2BatchTimeout is the interface to call the smart contract function
842func (c *Client) RollupUpdateForgeL1L2BatchTimeout(newForgeL1Timeout int64) (tx *types.Transaction, err error) {
843 c.rw.Lock()
844 defer c.rw.Unlock()
845 cpy := c.nextBlock().copy()
846 defer func() { c.revertIfErr(err, cpy) }()
847 if c.addr == nil {
848 return nil, eth.ErrAccountNil
849 }
850
851 log.Error("TODO")
852 return nil, errTODO
853}
854
855// RollupUpdateFeeAddToken is the interface to call the smart contract function
856func (c *Client) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (tx *types.Transaction, err error) {
857 c.rw.Lock()
858 defer c.rw.Unlock()
859 cpy := c.nextBlock().copy()
860 defer func() { c.revertIfErr(err, cpy) }()
861 if c.addr == nil {
862 return nil, eth.ErrAccountNil
863 }
864
865 log.Error("TODO")
866 return nil, errTODO
867}
868
869// RollupUpdateTokensHEZ is the interface to call the smart contract function
870// func (c *Client) RollupUpdateTokensHEZ(newTokenHEZ ethCommon.Address) (tx *types.Transaction, err error) {
871// c.rw.Lock()
872// defer c.rw.Unlock()
873// cpy := c.nextBlock().copy()
874// defer func() { c.revertIfErr(err, cpy) }()
875//
876// log.Error("TODO")
877// return nil, errTODO
878// }
879
880// RollupUpdateGovernance is the interface to call the smart contract function
881// func (c *Client) RollupUpdateGovernance() (*types.Transaction, error) { // TODO (Not defined in Hermez.sol)
882// return nil, errTODO
883// }
884
885// RollupConstants returns the Constants of the Rollup Smart Contract
886func (c *Client) RollupConstants() (*common.RollupConstants, error) {
887 c.rw.RLock()
888 defer c.rw.RUnlock()
889
890 return c.rollupConstants, nil
891}
892
893// RollupEventsByBlock returns the events in a block that happened in the Rollup Smart Contract
894func (c *Client) RollupEventsByBlock(blockNum int64) (*eth.RollupEvents, *ethCommon.Hash, error) {
895 c.rw.RLock()
896 defer c.rw.RUnlock()
897
898 block, ok := c.blocks[blockNum]
899 if !ok {
900 return nil, nil, fmt.Errorf("Block %v doesn't exist", blockNum)
901 }
902 return &block.Rollup.Events, &block.Eth.Hash, nil
903}
904
905// RollupForgeBatchArgs returns the arguments used in a ForgeBatch call in the Rollup Smart Contract in the given transaction
906func (c *Client) RollupForgeBatchArgs(ethTxHash ethCommon.Hash) (*eth.RollupForgeBatchArgs, *ethCommon.Address, error) {
907 c.rw.RLock()
908 defer c.rw.RUnlock()
909
910 batch, ok := c.forgeBatchArgs[ethTxHash]
911 if !ok {
912 return nil, nil, fmt.Errorf("transaction not found")
913 }
914 return &batch.ForgeBatchArgs, &batch.Sender, nil
915}
916
917//
918// Auction
919//
920
921// AuctionSetSlotDeadline is the interface to call the smart contract function
922func (c *Client) AuctionSetSlotDeadline(newDeadline uint8) (tx *types.Transaction, err error) {
923 c.rw.Lock()
924 defer c.rw.Unlock()
925 cpy := c.nextBlock().copy()
926 defer func() { c.revertIfErr(err, cpy) }()
927 if c.addr == nil {
928 return nil, eth.ErrAccountNil
929 }
930
931 log.Error("TODO")
932 return nil, errTODO
933}
934
935// AuctionGetSlotDeadline is the interface to call the smart contract function
936func (c *Client) AuctionGetSlotDeadline() (uint8, error) {
937 c.rw.RLock()
938 defer c.rw.RUnlock()
939
940 log.Error("TODO")
941 return 0, errTODO
942}
943
944// AuctionSetOpenAuctionSlots is the interface to call the smart contract function
945func (c *Client) AuctionSetOpenAuctionSlots(newOpenAuctionSlots uint16) (tx *types.Transaction, err error) {
946 c.rw.Lock()
947 defer c.rw.Unlock()
948 cpy := c.nextBlock().copy()
949 defer func() { c.revertIfErr(err, cpy) }()
950 if c.addr == nil {
951 return nil, eth.ErrAccountNil
952 }
953
954 log.Error("TODO")
955 return nil, errTODO
956}
957
958// AuctionGetOpenAuctionSlots is the interface to call the smart contract function
959func (c *Client) AuctionGetOpenAuctionSlots() (uint16, error) {
960 c.rw.RLock()
961 defer c.rw.RUnlock()
962
963 log.Error("TODO")
964 return 0, errTODO
965}
966
967// AuctionSetClosedAuctionSlots is the interface to call the smart contract function
968func (c *Client) AuctionSetClosedAuctionSlots(newClosedAuctionSlots uint16) (tx *types.Transaction, err error) {
969 c.rw.Lock()
970 defer c.rw.Unlock()
971 cpy := c.nextBlock().copy()
972 defer func() { c.revertIfErr(err, cpy) }()
973 if c.addr == nil {
974 return nil, eth.ErrAccountNil
975 }
976
977 log.Error("TODO")
978 return nil, errTODO
979}
980
981// AuctionGetClosedAuctionSlots is the interface to call the smart contract function
982func (c *Client) AuctionGetClosedAuctionSlots() (uint16, error) {
983 c.rw.RLock()
984 defer c.rw.RUnlock()
985
986 log.Error("TODO")
987 return 0, errTODO
988}
989
990// AuctionSetOutbidding is the interface to call the smart contract function
991func (c *Client) AuctionSetOutbidding(newOutbidding uint16) (tx *types.Transaction, err error) {
992 c.rw.Lock()
993 defer c.rw.Unlock()
994 cpy := c.nextBlock().copy()
995 defer func() { c.revertIfErr(err, cpy) }()
996 if c.addr == nil {
997 return nil, eth.ErrAccountNil
998 }
999
1000 log.Error("TODO")
1001 return nil, errTODO
1002}
1003
1004// AuctionGetOutbidding is the interface to call the smart contract function
1005func (c *Client) AuctionGetOutbidding() (uint16, error) {
1006 c.rw.RLock()
1007 defer c.rw.RUnlock()
1008
1009 log.Error("TODO")
1010 return 0, errTODO
1011}
1012
1013// AuctionSetAllocationRatio is the interface to call the smart contract function
1014func (c *Client) AuctionSetAllocationRatio(newAllocationRatio [3]uint16) (tx *types.Transaction, err error) {
1015 c.rw.Lock()
1016 defer c.rw.Unlock()
1017 cpy := c.nextBlock().copy()
1018 defer func() { c.revertIfErr(err, cpy) }()
1019 if c.addr == nil {
1020 return nil, eth.ErrAccountNil
1021 }
1022
1023 log.Error("TODO")
1024 return nil, errTODO
1025}
1026
1027// AuctionGetAllocationRatio is the interface to call the smart contract function
1028func (c *Client) AuctionGetAllocationRatio() ([3]uint16, error) {
1029 c.rw.RLock()
1030 defer c.rw.RUnlock()
1031
1032 log.Error("TODO")
1033 return [3]uint16{}, errTODO
1034}
1035
1036// AuctionSetDonationAddress is the interface to call the smart contract function
1037func (c *Client) AuctionSetDonationAddress(newDonationAddress ethCommon.Address) (tx *types.Transaction, err error) {
1038 c.rw.Lock()
1039 defer c.rw.Unlock()
1040 cpy := c.nextBlock().copy()
1041 defer func() { c.revertIfErr(err, cpy) }()
1042 if c.addr == nil {
1043 return nil, eth.ErrAccountNil
1044 }
1045
1046 log.Error("TODO")
1047 return nil, errTODO
1048}
1049
1050// AuctionGetDonationAddress is the interface to call the smart contract function
1051func (c *Client) AuctionGetDonationAddress() (*ethCommon.Address, error) {
1052 c.rw.RLock()
1053 defer c.rw.RUnlock()
1054
1055 log.Error("TODO")
1056 return nil, errTODO
1057}
1058
1059// AuctionSetBootCoordinator is the interface to call the smart contract function
1060func (c *Client) AuctionSetBootCoordinator(newBootCoordinator ethCommon.Address) (tx *types.Transaction, err error) {
1061 c.rw.Lock()
1062 defer c.rw.Unlock()
1063 cpy := c.nextBlock().copy()
1064 defer func() { c.revertIfErr(err, cpy) }()
1065 if c.addr == nil {
1066 return nil, eth.ErrAccountNil
1067 }
1068
1069 log.Error("TODO")
1070 return nil, errTODO
1071}
1072
1073// AuctionGetBootCoordinator is the interface to call the smart contract function
1074func (c *Client) AuctionGetBootCoordinator() (*ethCommon.Address, error) {
1075 c.rw.RLock()
1076 defer c.rw.RUnlock()
1077
1078 currentBlock := c.currentBlock()
1079 a := currentBlock.Auction
1080
1081 return &a.Vars.BootCoordinator, nil
1082}
1083
1084// AuctionChangeDefaultSlotSetBid is the interface to call the smart contract function
1085func (c *Client) AuctionChangeDefaultSlotSetBid(slotSet int64, newInitialMinBid *big.Int) (tx *types.Transaction, err error) {
1086 c.rw.Lock()
1087 defer c.rw.Unlock()
1088 cpy := c.nextBlock().copy()
1089 defer func() { c.revertIfErr(err, cpy) }()
1090 if c.addr == nil {
1091 return nil, eth.ErrAccountNil
1092 }
1093
1094 log.Error("TODO")
1095 return nil, errTODO
1096}
1097
1098// AuctionSetCoordinator is the interface to call the smart contract function
1099func (c *Client) AuctionSetCoordinator(forger ethCommon.Address, URL string) (tx *types.Transaction, err error) {
1100 c.rw.Lock()
1101 defer c.rw.Unlock()
1102 cpy := c.nextBlock().copy()
1103 defer func() { c.revertIfErr(err, cpy) }()
1104 if c.addr == nil {
1105 return nil, eth.ErrAccountNil
1106 }
1107
1108 nextBlock := c.nextBlock()
1109 a := nextBlock.Auction
1110
1111 a.State.Coordinators[*c.addr] = ð.Coordinator{
1112 Forger: forger,
1113 URL: URL,
1114 }
1115
1116 a.Events.SetCoordinator = append(a.Events.SetCoordinator,
1117 eth.AuctionEventSetCoordinator{
1118 BidderAddress: *c.addr,
1119 ForgerAddress: forger,
1120 CoordinatorURL: URL,
1121 })
1122
1123 type data struct {
1124 BidderAddress ethCommon.Address
1125 ForgerAddress ethCommon.Address
1126 URL string
1127 }
1128 return a.addTransaction(newTransaction("registercoordinator", data{*c.addr, forger, URL})), nil
1129}
1130
1131// AuctionIsRegisteredCoordinator is the interface to call the smart contract function
1132func (c *Client) AuctionIsRegisteredCoordinator(forgerAddress ethCommon.Address) (bool, error) {
1133 c.rw.RLock()
1134 defer c.rw.RUnlock()
1135
1136 log.Error("TODO")
1137 return false, errTODO
1138}
1139
1140// AuctionUpdateCoordinatorInfo is the interface to call the smart contract function
1141func (c *Client) AuctionUpdateCoordinatorInfo(forgerAddress ethCommon.Address, newWithdrawAddress ethCommon.Address, newURL string) (tx *types.Transaction, err error) {
1142 c.rw.Lock()
1143 defer c.rw.Unlock()
1144 cpy := c.nextBlock().copy()
1145 defer func() { c.revertIfErr(err, cpy) }()
1146 if c.addr == nil {
1147 return nil, eth.ErrAccountNil
1148 }
1149
1150 log.Error("TODO")
1151 return nil, errTODO
1152}
1153
1154// AuctionGetSlotNumber is the interface to call the smart contract function
1155func (c *Client) AuctionGetSlotNumber(blockNum int64) (int64, error) {
1156 c.rw.RLock()
1157 defer c.rw.RUnlock()
1158
1159 currentBlock := c.currentBlock()
1160 a := currentBlock.Auction
1161 return a.getSlotNumber(blockNum), nil
1162}
1163
1164// AuctionGetCurrentSlotNumber is the interface to call the smart contract function
1165func (c *Client) AuctionGetCurrentSlotNumber() (int64, error) {
1166 c.rw.RLock()
1167 defer c.rw.RUnlock()
1168
1169 log.Error("TODO")
1170 return 0, errTODO
1171}
1172
1173// AuctionGetMinBidBySlot is the interface to call the smart contract function
1174func (c *Client) AuctionGetMinBidBySlot(slot int64) (*big.Int, error) {
1175 c.rw.RLock()
1176 defer c.rw.RUnlock()
1177
1178 log.Error("TODO")
1179 return nil, errTODO
1180}
1181
1182// AuctionGetDefaultSlotSetBid is the interface to call the smart contract function
1183func (c *Client) AuctionGetDefaultSlotSetBid(slotSet uint8) (*big.Int, error) {
1184 c.rw.RLock()
1185 defer c.rw.RUnlock()
1186
1187 log.Error("TODO")
1188 return nil, errTODO
1189}
1190
1191// AuctionGetSlotSet is the interface to call the smart contract function
1192func (c *Client) AuctionGetSlotSet(slot int64) (*big.Int, error) {
1193 c.rw.RLock()
1194 defer c.rw.RUnlock()
1195
1196 log.Error("TODO")
1197 return nil, errTODO
1198}
1199
1200// AuctionTokensReceived is the interface to call the smart contract function
1201// func (c *Client) AuctionTokensReceived(operator, from, to ethCommon.Address, amount *big.Int, userData, operatorData []byte) error {
1202// return errTODO
1203// }
1204
1205// AuctionBidSimple is a wrapper around AuctionBid that automatically sets `amount` and `deadline`.
1206func (c *Client) AuctionBidSimple(slot int64, bidAmount *big.Int) (tx *types.Transaction, err error) {
1207 return c.AuctionBid(bidAmount, slot, bidAmount, big.NewInt(99999)) //nolint:gomnd
1208}
1209
1210// AuctionBid is the interface to call the smart contract function. This
1211// implementation behaves as if any address has infinite tokens.
1212func (c *Client) AuctionBid(amount *big.Int, slot int64, bidAmount *big.Int,
1213 deadline *big.Int) (tx *types.Transaction, err error) {
1214 c.rw.Lock()
1215 defer c.rw.Unlock()
1216 cpy := c.nextBlock().copy()
1217 defer func() { func() { c.revertIfErr(err, cpy) }() }()
1218 if c.addr == nil {
1219 return nil, eth.ErrAccountNil
1220 }
1221
1222 nextBlock := c.nextBlock()
1223 a := nextBlock.Auction
1224
1225 if slot < a.getCurrentSlotNumber()+int64(a.Vars.ClosedAuctionSlots) {
1226 return nil, errBidClosed
1227 }
1228
1229 if slot >= a.getCurrentSlotNumber()+int64(a.Vars.ClosedAuctionSlots)+int64(a.Vars.OpenAuctionSlots) {
1230 return nil, errBidNotOpen
1231 }
1232
1233 minBid, err := a.getMinBidBySlot(slot)
1234 if err != nil {
1235 return nil, err
1236 }
1237 if bidAmount.Cmp(minBid) == -1 {
1238 return nil, errBidBelowMin
1239 }
1240
1241 if _, ok := a.State.Coordinators[*c.addr]; !ok {
1242 return nil, errCoordNotReg
1243 }
1244
1245 slotState, ok := a.State.Slots[slot]
1246 if !ok {
1247 slotState = eth.NewSlotState()
1248 a.State.Slots[slot] = slotState
1249 }
1250 slotState.Bidder = *c.addr
1251 slotState.BidAmount = bidAmount
1252
1253 a.Events.NewBid = append(a.Events.NewBid,
1254 eth.AuctionEventNewBid{Slot: slot, BidAmount: bidAmount, Bidder: *c.addr})
1255
1256 type data struct {
1257 Slot int64
1258 BidAmount *big.Int
1259 Bidder ethCommon.Address
1260 }
1261 return a.addTransaction(newTransaction("bid", data{slot, bidAmount, *c.addr})), nil
1262}
1263
1264// AuctionMultiBid is the interface to call the smart contract function. This
1265// implementation behaves as if any address has infinite tokens.
1266func (c *Client) AuctionMultiBid(amount *big.Int, startingSlot int64, endingSlot int64, slotSet [6]bool,
1267 maxBid, closedMinBid, deadline *big.Int) (tx *types.Transaction, err error) {
1268 c.rw.Lock()
1269 defer c.rw.Unlock()
1270 cpy := c.nextBlock().copy()
1271 defer func() { c.revertIfErr(err, cpy) }()
1272 if c.addr == nil {
1273 return nil, eth.ErrAccountNil
1274 }
1275
1276 log.Error("TODO")
1277 return nil, errTODO
1278}
1279
1280// AuctionCanForge is the interface to call the smart contract function
1281func (c *Client) AuctionCanForge(forger ethCommon.Address, blockNum int64) (bool, error) {
1282 c.rw.RLock()
1283 defer c.rw.RUnlock()
1284
1285 currentBlock := c.currentBlock()
1286 a := currentBlock.Auction
1287 return a.canForge(forger, blockNum)
1288}
1289
1290// AuctionForge is the interface to call the smart contract function
1291func (c *Client) AuctionForge(forger ethCommon.Address) (tx *types.Transaction, err error) {
1292 c.rw.Lock()
1293 defer c.rw.Unlock()
1294 cpy := c.nextBlock().copy()
1295 defer func() { c.revertIfErr(err, cpy) }()
1296 if c.addr == nil {
1297 return nil, eth.ErrAccountNil
1298 }
1299
1300 log.Error("TODO")
1301 return nil, errTODO
1302}
1303
1304// AuctionClaimHEZ is the interface to call the smart contract function
1305func (c *Client) AuctionClaimHEZ() (tx *types.Transaction, err error) {
1306 c.rw.Lock()
1307 defer c.rw.Unlock()
1308 cpy := c.nextBlock().copy()
1309 defer func() { c.revertIfErr(err, cpy) }()
1310 if c.addr == nil {
1311 return nil, eth.ErrAccountNil
1312 }
1313
1314 log.Error("TODO")
1315 return nil, errTODO
1316}
1317
1318// AuctionGetClaimableHEZ is the interface to call the smart contract function
1319func (c *Client) AuctionGetClaimableHEZ(bidder ethCommon.Address) (*big.Int, error) {
1320 c.rw.RLock()
1321 defer c.rw.RUnlock()
1322
1323 log.Error("TODO")
1324 return nil, errTODO
1325}
1326
1327// AuctionConstants returns the Constants of the Auction Smart Contract
1328func (c *Client) AuctionConstants() (*common.AuctionConstants, error) {
1329 c.rw.RLock()
1330 defer c.rw.RUnlock()
1331
1332 return c.auctionConstants, nil
1333}
1334
1335// AuctionEventsByBlock returns the events in a block that happened in the Auction Smart Contract
1336func (c *Client) AuctionEventsByBlock(blockNum int64) (*eth.AuctionEvents, *ethCommon.Hash, error) {
1337 c.rw.RLock()
1338 defer c.rw.RUnlock()
1339
1340 block, ok := c.blocks[blockNum]
1341 if !ok {
1342 return nil, nil, fmt.Errorf("Block %v doesn't exist", blockNum)
1343 }
1344 return &block.Auction.Events, &block.Eth.Hash, nil
1345}