mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 03:16:45 +01:00
Merge pull request #145 from hermeznetwork/feature/integration
Update test ethclient and coordinator
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package coordinator
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
)
|
||||
|
||||
@@ -18,6 +19,7 @@ type BatchInfo struct {
|
||||
L1OperatorTxs []*common.L1Tx
|
||||
L2Txs []*common.PoolL2Tx
|
||||
// FeesInfo
|
||||
ethTx *types.Transaction
|
||||
}
|
||||
|
||||
// NewBatchInfo creates a new BatchInfo with the given batchNum &
|
||||
@@ -52,3 +54,8 @@ func (bi *BatchInfo) SetServerProof(serverProof ServerProofInterface) {
|
||||
func (bi *BatchInfo) SetProof(proof *Proof) {
|
||||
bi.proof = proof
|
||||
}
|
||||
|
||||
// SetEthTx sets the ethTx to the BatchInfo data structure
|
||||
func (bi *BatchInfo) SetEthTx(ethTx *types.Transaction) {
|
||||
bi.ethTx = ethTx
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package coordinator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/hermeznetwork/hermez-node/batchbuilder"
|
||||
"github.com/hermeznetwork/hermez-node/common"
|
||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||
"github.com/hermeznetwork/hermez-node/eth"
|
||||
"github.com/hermeznetwork/hermez-node/log"
|
||||
"github.com/hermeznetwork/hermez-node/txselector"
|
||||
kvdb "github.com/iden3/go-merkletree/db"
|
||||
"github.com/iden3/go-merkletree/db/memory"
|
||||
)
|
||||
|
||||
var errTODO = fmt.Errorf("TODO")
|
||||
@@ -28,9 +28,9 @@ type Config struct {
|
||||
|
||||
// Coordinator implements the Coordinator type
|
||||
type Coordinator struct {
|
||||
forging bool
|
||||
rw *sync.RWMutex
|
||||
isForgeSeq bool // WIP just for testing while implementing
|
||||
forging bool
|
||||
// rw *sync.RWMutex
|
||||
// isForgeSeq bool // WIP just for testing while implementing
|
||||
|
||||
config Config
|
||||
|
||||
@@ -42,8 +42,9 @@ type Coordinator struct {
|
||||
txsel *txselector.TxSelector
|
||||
batchBuilder *batchbuilder.BatchBuilder
|
||||
|
||||
ethClient eth.ClientInterface
|
||||
ethTxStore kvdb.Storage
|
||||
ethClient eth.ClientInterface
|
||||
ethTxs []*types.Transaction
|
||||
// ethTxStore kvdb.Storage
|
||||
}
|
||||
|
||||
// NewCoordinator creates a new Coordinator
|
||||
@@ -64,17 +65,30 @@ func NewCoordinator(conf Config,
|
||||
txsel: txsel,
|
||||
batchBuilder: bb,
|
||||
ethClient: ethClient,
|
||||
ethTxStore: memory.NewMemoryStorage(),
|
||||
rw: &sync.RWMutex{},
|
||||
|
||||
ethTxs: make([]*types.Transaction, 0),
|
||||
// ethTxStore: memory.NewMemoryStorage(),
|
||||
// rw: &sync.RWMutex{},
|
||||
}
|
||||
return &c
|
||||
}
|
||||
|
||||
// TODO(Edu): Change the current design of the coordinator structur:
|
||||
// - Move Start and Stop functions (from node/node.go) here
|
||||
// - Add concept of StartPipeline, StopPipeline, that spawns and stops the goroutines
|
||||
// - Add a Manager that calls StartPipeline and StopPipeline, checks when it's time to forge, schedules new batches, etc.
|
||||
// - Add a TxMonitor that monitors successful ForgeBatch ethereum transactions and waits for N blocks of confirmation, and reports back errors to the Manager.
|
||||
|
||||
// ForgeLoopFn is the function ran in a loop that checks if it's time to forge
|
||||
// and forges a batch if so and sends it to outBatchCh. Returns true if it's
|
||||
// the coordinator turn to forge.
|
||||
func (c *Coordinator) ForgeLoopFn(outBatchCh chan *BatchInfo, stopCh chan bool) (forgetime bool, err error) {
|
||||
if !c.isForgeSequence() {
|
||||
// TODO: Move the logic to check if it's forge time or not outside the pipeline
|
||||
isForgeSequence, err := c.isForgeSequence()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !isForgeSequence {
|
||||
if c.forging {
|
||||
log.Info("ForgeLoopFn: forging state end")
|
||||
c.forging = false
|
||||
@@ -106,6 +120,12 @@ func (c *Coordinator) ForgeLoopFn(outBatchCh chan *BatchInfo, stopCh chan bool)
|
||||
// if c.synchronizer.Reorg():
|
||||
_ = c.handleReorg()
|
||||
|
||||
defer func() {
|
||||
if err == ErrStop {
|
||||
log.Info("ForgeLoopFn: forgeLoopFn stopped")
|
||||
}
|
||||
}()
|
||||
|
||||
// 0. Wait for an available server proof
|
||||
// blocking call
|
||||
serverProof, err := c.serverProofPool.Get(stopCh)
|
||||
@@ -134,10 +154,14 @@ func (c *Coordinator) ForgeLoopFn(outBatchCh chan *BatchInfo, stopCh chan bool)
|
||||
// GetProofCallForgeLoopFn is the function ran in a loop that gets a forged
|
||||
// batch via inBatchCh, waits for the proof server to finish, calls the ForgeBatch
|
||||
// function in the Rollup Smart Contract, and sends the batch to outBatchCh.
|
||||
func (c *Coordinator) GetProofCallForgeLoopFn(inBatchCh, outBatchCh chan *BatchInfo, stopCh chan bool) error {
|
||||
func (c *Coordinator) GetProofCallForgeLoopFn(inBatchCh, outBatchCh chan *BatchInfo, stopCh chan bool) (err error) {
|
||||
defer func() {
|
||||
if err == ErrStop {
|
||||
log.Info("GetProofCallForgeLoopFn: forgeLoopFn stopped")
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-stopCh:
|
||||
log.Info("GetProofCallForgeLoopFn: forgeLoopFn stopped")
|
||||
return ErrStop
|
||||
case batchInfo := <-inBatchCh:
|
||||
log.Debugw("GetProofCallForgeLoopFn: getProofCallForge start", "batchNum", batchInfo.batchNum)
|
||||
@@ -153,14 +177,18 @@ func (c *Coordinator) GetProofCallForgeLoopFn(inBatchCh, outBatchCh chan *BatchI
|
||||
// ForgeCallConfirmLoopFn is the function ran in a loop that gets a batch that
|
||||
// has been sent to the Rollup Smart Contract via inBatchCh and waits for the
|
||||
// ethereum transaction confirmation.
|
||||
func (c *Coordinator) ForgeCallConfirmLoopFn(inBatchCh chan *BatchInfo, stopCh chan bool) error {
|
||||
func (c *Coordinator) ForgeCallConfirmLoopFn(inBatchCh chan *BatchInfo, stopCh chan bool) (err error) {
|
||||
defer func() {
|
||||
if err == ErrStop {
|
||||
log.Info("ForgeCallConfirmLoopFn: forgeConfirmLoopFn stopped")
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-stopCh:
|
||||
log.Info("ForgeCallConfirmLoopFn: forgeConfirmLoopFn stopped")
|
||||
return ErrStop
|
||||
case batchInfo := <-inBatchCh:
|
||||
log.Debugw("ForgeCallConfirmLoopFn: forgeCallConfirm start", "batchNum", batchInfo.batchNum)
|
||||
if err := c.forgeCallConfirm(batchInfo); err != nil {
|
||||
if err := c.forgeCallConfirm(batchInfo, stopCh); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugw("ForgeCallConfirmLoopFn: forgeCallConfirm end", "batchNum", batchInfo.batchNum)
|
||||
@@ -244,13 +272,15 @@ func (c *Coordinator) getProofCallForge(batchInfo *BatchInfo, stopCh chan bool)
|
||||
}
|
||||
batchInfo.SetProof(proof)
|
||||
forgeBatchArgs := c.prepareForgeBatchArgs(batchInfo)
|
||||
_, err = c.ethClient.RollupForgeBatch(forgeBatchArgs)
|
||||
ethTx, err := c.ethClient.RollupForgeBatch(forgeBatchArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: Move this to the next step (forgeCallConfirm)
|
||||
log.Debugf("ethClient ForgeCall sent, batchNum: %d", c.batchNum)
|
||||
batchInfo.SetEthTx(ethTx)
|
||||
|
||||
// TODO once tx data type is defined, store ethTx (returned by ForgeCall)
|
||||
// TODO(FUTURE) once tx data type is defined, store ethTx (returned by ForgeCall)
|
||||
// TBD if use ethTxStore as a disk k-v database, or use a Queue
|
||||
// tx, err := c.ethTxStore.NewTx()
|
||||
// if err != nil {
|
||||
@@ -264,13 +294,46 @@ func (c *Coordinator) getProofCallForge(batchInfo *BatchInfo, stopCh chan bool)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Coordinator) forgeCallConfirm(batchInfo *BatchInfo) error {
|
||||
func (c *Coordinator) forgeCallConfirm(batchInfo *BatchInfo, stopCh chan bool) error {
|
||||
// TODO strategy of this sequence TBD
|
||||
// confirm eth txs and mark them as accepted sequence
|
||||
// IDEA: Keep an array in Coordinator with the list of sent ethTx.
|
||||
// Here, loop over them and only delete them once the number of
|
||||
// confirmed blocks is over a configured value. If the tx is rejected,
|
||||
// return error.
|
||||
// ethTx := ethTxStore.GetFirstPending()
|
||||
// waitForAccepted(ethTx) // blocking call, returns once the ethTx is mined
|
||||
// ethTxStore.MarkAccepted(ethTx)
|
||||
return nil
|
||||
txID := batchInfo.ethTx.Hash()
|
||||
// TODO: Follow EthereumClient.waitReceipt logic
|
||||
count := 0
|
||||
// TODO: Define this waitTime in the config
|
||||
waitTime := 100 * time.Millisecond //nolint:gomnd
|
||||
select {
|
||||
case <-time.After(waitTime):
|
||||
receipt, err := c.ethClient.EthTransactionReceipt(context.TODO(), txID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if receipt != nil {
|
||||
if receipt.Status == types.ReceiptStatusFailed {
|
||||
return fmt.Errorf("receipt status is failed")
|
||||
} else if receipt.Status == types.ReceiptStatusSuccessful {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// TODO: Call go-ethereum:
|
||||
// if err == nil && receipt == nil :
|
||||
// `func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {`
|
||||
count++
|
||||
if time.Duration(count)*waitTime > 60*time.Second {
|
||||
log.Warnw("Waiting for ethTx receipt for more than 60 seconds", "tx", batchInfo.ethTx)
|
||||
// TODO: Decide if we resend the Tx with higher gas price
|
||||
}
|
||||
case <-stopCh:
|
||||
return ErrStop
|
||||
}
|
||||
return fmt.Errorf("timeout")
|
||||
}
|
||||
|
||||
func (c *Coordinator) handleReorg() error {
|
||||
@@ -278,10 +341,17 @@ func (c *Coordinator) handleReorg() error {
|
||||
}
|
||||
|
||||
// isForgeSequence returns true if the node is the Forger in the current ethereum block
|
||||
func (c *Coordinator) isForgeSequence() bool {
|
||||
c.rw.RLock()
|
||||
defer c.rw.RUnlock()
|
||||
return c.isForgeSeq // TODO
|
||||
func (c *Coordinator) isForgeSequence() (bool, error) {
|
||||
// TODO: Consider checking if we can forge by quering the Synchronizer instead of using ethClient
|
||||
blockNum, err := c.ethClient.EthCurrentBlock()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
addr, err := c.ethClient.EthAddress()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return c.ethClient.AuctionCanForge(*addr, blockNum+1)
|
||||
}
|
||||
|
||||
func (c *Coordinator) purgeRemoveByTimeout() error {
|
||||
|
||||
@@ -2,10 +2,12 @@ package coordinator
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/hermeznetwork/hermez-node/batchbuilder"
|
||||
dbUtils "github.com/hermeznetwork/hermez-node/db"
|
||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||
@@ -65,8 +67,9 @@ func (cn *CoordNode) Start() {
|
||||
cn.stopForge = make(chan bool)
|
||||
cn.stopGetProofCallForge = make(chan bool)
|
||||
cn.stopForgeCallConfirm = make(chan bool)
|
||||
batchCh0 := make(chan *BatchInfo)
|
||||
batchCh1 := make(chan *BatchInfo)
|
||||
queueSize := 8
|
||||
batchCh0 := make(chan *BatchInfo, queueSize)
|
||||
batchCh1 := make(chan *BatchInfo, queueSize)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
@@ -78,6 +81,7 @@ func (cn *CoordNode) Start() {
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Errorw("CoordNode ForgeLoopFn", "error", err)
|
||||
time.Sleep(200 * time.Millisecond) // Avoid overflowing log with errors
|
||||
} else if !forge {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
@@ -133,17 +137,38 @@ func (t *timer) Time() int64 {
|
||||
return currentTime
|
||||
}
|
||||
|
||||
func waitForSlot(t *testing.T, c *test.Client, slot int64) {
|
||||
for {
|
||||
blockNum, err := c.EthCurrentBlock()
|
||||
require.Nil(t, err)
|
||||
nextBlockSlot, err := c.AuctionGetSlotNumber(blockNum + 1)
|
||||
require.Nil(t, err)
|
||||
if nextBlockSlot == slot {
|
||||
break
|
||||
}
|
||||
c.CtlMineBlock()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoordinator(t *testing.T) {
|
||||
txsel, bb := newTestModules(t)
|
||||
|
||||
conf := Config{}
|
||||
hdb := &historydb.HistoryDB{}
|
||||
serverProofs := []ServerProofInterface{&ServerProof{}, &ServerProof{}}
|
||||
serverProofs := []ServerProofInterface{&ServerProofMock{}, &ServerProofMock{}}
|
||||
|
||||
var timer timer
|
||||
ethClientSetup := test.NewClientSetupExample()
|
||||
addr := ethClientSetup.AuctionVariables.BootCoordinator
|
||||
ethClient := test.NewClient(true, &timer, addr, ethClientSetup)
|
||||
addr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
||||
ethClient := test.NewClient(true, &timer, &addr, ethClientSetup)
|
||||
|
||||
// Bid for slot 2 and 4
|
||||
_, err := ethClient.AuctionRegisterCoordinator(addr, "https://foo.bar")
|
||||
require.Nil(t, err)
|
||||
_, err = ethClient.AuctionBid(2, big.NewInt(9999), addr)
|
||||
require.Nil(t, err)
|
||||
_, err = ethClient.AuctionBid(4, big.NewInt(9999), addr)
|
||||
require.Nil(t, err)
|
||||
|
||||
c := NewCoordinator(conf, hdb, txsel, bb, serverProofs, ethClient)
|
||||
cn := NewCoordNode(c)
|
||||
@@ -151,24 +176,18 @@ func TestCoordinator(t *testing.T) {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// simulate forgeSequence time
|
||||
waitForSlot(t, ethClient, 2)
|
||||
log.Info("simulate entering in forge time")
|
||||
c.rw.Lock()
|
||||
c.isForgeSeq = true
|
||||
c.rw.Unlock()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// simulate going out from forgeSequence
|
||||
waitForSlot(t, ethClient, 3)
|
||||
log.Info("simulate going out from forge time")
|
||||
c.rw.Lock()
|
||||
c.isForgeSeq = false
|
||||
c.rw.Unlock()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// simulate entering forgeSequence time again
|
||||
waitForSlot(t, ethClient, 4)
|
||||
log.Info("simulate entering in forge time again")
|
||||
c.rw.Lock()
|
||||
c.isForgeSeq = true
|
||||
c.rw.Unlock()
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// simulate stopping forgerLoop by channel
|
||||
|
||||
@@ -28,11 +28,13 @@ func NewServerProof(URL string) *ServerProof {
|
||||
// CalculateProof sends the *common.ZKInputs to the ServerProof to compute the
|
||||
// Proof
|
||||
func (p *ServerProof) CalculateProof(zkInputs *common.ZKInputs) error {
|
||||
log.Error("TODO")
|
||||
return errTODO
|
||||
}
|
||||
|
||||
// GetProof retreives the Proof from the ServerProof
|
||||
func (p *ServerProof) GetProof(stopCh chan bool) (*Proof, error) {
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
}
|
||||
|
||||
|
||||
@@ -240,11 +240,11 @@ type AuctionInterface interface {
|
||||
AuctionUpdateCoordinatorInfo(forgerAddress ethCommon.Address, newWithdrawAddress ethCommon.Address, newURL string) (*types.Transaction, error)
|
||||
|
||||
// Slot Info
|
||||
AuctionGetSlotNumber(blockNum int64) (int64, error)
|
||||
AuctionGetCurrentSlotNumber() (int64, error)
|
||||
AuctionGetMinBidBySlot(slot int64) (*big.Int, error)
|
||||
AuctionGetDefaultSlotSetBid(slotSet uint8) (*big.Int, error)
|
||||
AuctionGetSlotSet(slot int64) (*big.Int, error)
|
||||
AuctionGetSlotNumber(blockNum int64) (*big.Int, error)
|
||||
|
||||
// Bidding
|
||||
// AuctionTokensReceived(operator, from, to ethCommon.Address, amount *big.Int,
|
||||
@@ -698,7 +698,7 @@ func (c *AuctionClient) AuctionGetDefaultSlotSetBid(slotSet uint8) (*big.Int, er
|
||||
}
|
||||
|
||||
// AuctionGetSlotNumber is the interface to call the smart contract function
|
||||
func (c *AuctionClient) AuctionGetSlotNumber(blockNum int64) (*big.Int, error) {
|
||||
func (c *AuctionClient) AuctionGetSlotNumber(blockNum int64) (int64, error) {
|
||||
var slot *big.Int
|
||||
if err := c.client.Call(func(ec *ethclient.Client) error {
|
||||
auction, err := HermezAuctionProtocol.NewHermezAuctionProtocol(c.address, ec)
|
||||
@@ -709,9 +709,9 @@ func (c *AuctionClient) AuctionGetSlotNumber(blockNum int64) (*big.Int, error) {
|
||||
slot, err = auction.GetSlotNumber(nil, blockNumBig)
|
||||
return err
|
||||
}); err != nil {
|
||||
return big.NewInt(0), err
|
||||
return 0, err
|
||||
}
|
||||
return slot, nil
|
||||
return slot.Int64(), nil
|
||||
}
|
||||
|
||||
// AuctionBid is the interface to call the smart contract function
|
||||
|
||||
@@ -21,6 +21,8 @@ type EthereumInterface interface {
|
||||
EthCurrentBlock() (int64, error)
|
||||
// EthHeaderByNumber(context.Context, *big.Int) (*types.Header, error)
|
||||
EthBlockByNumber(context.Context, int64) (*common.Block, error)
|
||||
EthAddress() (*ethCommon.Address, error)
|
||||
EthTransactionReceipt(context.Context, ethCommon.Hash) (*types.Receipt, error)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -90,6 +92,14 @@ func (c *EthereumClient) Account() *accounts.Account {
|
||||
return c.account
|
||||
}
|
||||
|
||||
// EthAddress returns the ethereum address of the account loaded into the EthereumClient
|
||||
func (c *EthereumClient) EthAddress() (*ethCommon.Address, error) {
|
||||
if c.account == nil {
|
||||
return nil, ErrAccountNil
|
||||
}
|
||||
return &c.account.Address, nil
|
||||
}
|
||||
|
||||
// CallAuth performs a Smart Contract method call that requires authorization.
|
||||
// This call requires a valid account with Ether that can be spend during the
|
||||
// call.
|
||||
@@ -186,16 +196,21 @@ func (c *EthereumClient) GetReceipt(tx *types.Transaction) (*types.Receipt, erro
|
||||
return c.waitReceipt(ctx, tx, 0)
|
||||
}
|
||||
|
||||
// EthTransactionReceipt returns the transaction receipt of the given txHash
|
||||
func (c *EthereumClient) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) {
|
||||
return c.client.TransactionReceipt(ctx, txHash)
|
||||
}
|
||||
|
||||
func (c *EthereumClient) waitReceipt(ctx context.Context, tx *types.Transaction, timeout time.Duration) (*types.Receipt, error) {
|
||||
var err error
|
||||
var receipt *types.Receipt
|
||||
|
||||
txid := tx.Hash()
|
||||
log.Debugw("Waiting for receipt", "tx", txid.Hex())
|
||||
txHash := tx.Hash()
|
||||
log.Debugw("Waiting for receipt", "tx", txHash.Hex())
|
||||
|
||||
start := time.Now()
|
||||
for {
|
||||
receipt, err = c.client.TransactionReceipt(ctx, txid)
|
||||
receipt, err = c.client.TransactionReceipt(ctx, txHash)
|
||||
if receipt != nil || time.Since(start) >= timeout {
|
||||
break
|
||||
}
|
||||
@@ -203,15 +218,15 @@ func (c *EthereumClient) waitReceipt(ctx context.Context, tx *types.Transaction,
|
||||
}
|
||||
|
||||
if receipt != nil && receipt.Status == types.ReceiptStatusFailed {
|
||||
log.Errorw("Failed transaction", "tx", txid.Hex())
|
||||
log.Errorw("Failed transaction", "tx", txHash.Hex())
|
||||
return receipt, ErrReceiptStatusFailed
|
||||
}
|
||||
|
||||
if receipt == nil {
|
||||
log.Debugw("Pendingtransaction / Wait receipt timeout", "tx", txid.Hex(), "lasterr", err)
|
||||
log.Debugw("Pendingtransaction / Wait receipt timeout", "tx", txHash.Hex(), "lasterr", err)
|
||||
return receipt, ErrReceiptNotReceived
|
||||
}
|
||||
log.Debugw("Successful transaction", "tx", txid.Hex())
|
||||
log.Debugw("Successful transaction", "tx", txHash.Hex())
|
||||
|
||||
return receipt, err
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@@ -24,5 +24,6 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/tools/gopls v0.5.0 // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1
|
||||
)
|
||||
|
||||
30
go.sum
30
go.sum
@@ -234,6 +234,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -494,6 +496,7 @@ github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
|
||||
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=
|
||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI=
|
||||
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
@@ -510,6 +513,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I=
|
||||
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
@@ -581,6 +586,8 @@ github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+m
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
@@ -648,6 +655,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -672,6 +681,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -681,6 +692,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -732,15 +745,24 @@ golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE=
|
||||
golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200914163123-ea50a3c84940 h1:151ExL+g/k/wnhOqV+O1OliaTi0FR2UxQEEcpAhzzw8=
|
||||
golang.org/x/tools v0.0.0-20200914163123-ea50a3c84940/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20200929223013-bf155c11ec6f h1:7+Nz9MyPqt2qMCTvNiRy1G0zYfkB7UCa+ayT6uVvbyI=
|
||||
golang.org/x/tools/gopls v0.5.0 h1:XEmO9RylgmaXp33iGrWfCGopVYDGBmLy+KmsIsfIo8Y=
|
||||
golang.org/x/tools/gopls v0.5.0/go.mod h1:bm7s/5W/faSLxWyOWFtTI+5lZQQVdtksvEXdIfkFE74=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@@ -764,6 +786,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
@@ -796,6 +819,7 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
@@ -808,5 +832,11 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
mvdan.cc/gofumpt v0.0.0-20200802201014-ab5a8192947d h1:t8TAw9WgTLghti7RYkpPmqk4JtQ3+wcP5GgZqgWeWLQ=
|
||||
mvdan.cc/gofumpt v0.0.0-20200802201014-ab5a8192947d/go.mod h1:bzrjFmaD6+xqohD3KYP0H2FEuxknnBmyyOxdhLdaIws=
|
||||
mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A=
|
||||
mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
||||
@@ -144,8 +144,9 @@ func (n *Node) StartCoordinator() {
|
||||
n.stoppedGetProofCallForge = make(chan bool)
|
||||
n.stoppedForgeCallConfirm = make(chan bool)
|
||||
|
||||
batchCh0 := make(chan *coordinator.BatchInfo)
|
||||
batchCh1 := make(chan *coordinator.BatchInfo)
|
||||
queueSize := 1
|
||||
batchCh0 := make(chan *coordinator.BatchInfo, queueSize)
|
||||
batchCh1 := make(chan *coordinator.BatchInfo, queueSize)
|
||||
|
||||
go func() {
|
||||
defer func() { n.stoppedForge <- true }()
|
||||
|
||||
@@ -33,10 +33,17 @@ type RollupBlock struct {
|
||||
State eth.RollupState
|
||||
Vars eth.RollupVariables
|
||||
Events eth.RollupEvents
|
||||
Txs map[ethCommon.Hash]*types.Transaction
|
||||
Constants *eth.RollupConstants
|
||||
Eth *EthereumBlock
|
||||
}
|
||||
|
||||
func (r *RollupBlock) addTransaction(tx *types.Transaction) *types.Transaction {
|
||||
txHash := tx.Hash()
|
||||
r.Txs[txHash] = tx
|
||||
return tx
|
||||
}
|
||||
|
||||
var (
|
||||
errBidClosed = fmt.Errorf("Bid has already been closed")
|
||||
errBidNotOpen = fmt.Errorf("Bid has not been opened yet")
|
||||
@@ -49,10 +56,17 @@ type AuctionBlock struct {
|
||||
State eth.AuctionState
|
||||
Vars eth.AuctionVariables
|
||||
Events eth.AuctionEvents
|
||||
Txs map[ethCommon.Hash]*types.Transaction
|
||||
Constants *eth.AuctionConstants
|
||||
Eth *EthereumBlock
|
||||
}
|
||||
|
||||
func (a *AuctionBlock) addTransaction(tx *types.Transaction) *types.Transaction {
|
||||
txHash := tx.Hash()
|
||||
a.Txs[txHash] = tx
|
||||
return tx
|
||||
}
|
||||
|
||||
func (a *AuctionBlock) getSlotNumber(blockNumber int64) int64 {
|
||||
if a.Eth.BlockNum >= a.Constants.GenesisBlockNum {
|
||||
return (blockNumber - a.Constants.GenesisBlockNum) / int64(a.Constants.BlocksPerSlot)
|
||||
@@ -286,7 +300,7 @@ type Timer interface {
|
||||
type Client struct {
|
||||
rw *sync.RWMutex
|
||||
log bool
|
||||
addr ethCommon.Address
|
||||
addr *ethCommon.Address
|
||||
rollupConstants *eth.RollupConstants
|
||||
auctionConstants *eth.AuctionConstants
|
||||
blocks map[int64]*Block
|
||||
@@ -302,7 +316,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, addr ethCommon.Address, setup *ClientSetup) *Client {
|
||||
func NewClient(l bool, timer Timer, addr *ethCommon.Address, setup *ClientSetup) *Client {
|
||||
blocks := make(map[int64]*Block)
|
||||
blockNum := int64(0)
|
||||
|
||||
@@ -326,6 +340,7 @@ func NewClient(l bool, timer Timer, addr ethCommon.Address, setup *ClientSetup)
|
||||
CurrentIdx: 0,
|
||||
},
|
||||
Vars: *setup.RollupVariables,
|
||||
Txs: make(map[ethCommon.Hash]*types.Transaction),
|
||||
Events: eth.NewRollupEvents(),
|
||||
Constants: setup.RollupConstants,
|
||||
},
|
||||
@@ -336,6 +351,7 @@ func NewClient(l bool, timer Timer, addr ethCommon.Address, setup *ClientSetup)
|
||||
Coordinators: make(map[ethCommon.Address]*eth.Coordinator),
|
||||
},
|
||||
Vars: *setup.AuctionVariables,
|
||||
Txs: make(map[ethCommon.Hash]*types.Transaction),
|
||||
Events: eth.NewAuctionEvents(),
|
||||
Constants: setup.AuctionConstants,
|
||||
},
|
||||
@@ -468,6 +484,30 @@ func (c *Client) EthCurrentBlock() (int64, error) {
|
||||
return c.blockNum, nil
|
||||
}
|
||||
|
||||
// EthTransactionReceipt returns the transaction receipt of the given txHash
|
||||
func (c *Client) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) {
|
||||
c.rw.RLock()
|
||||
defer c.rw.RUnlock()
|
||||
|
||||
for i := int64(0); i < c.blockNum; i++ {
|
||||
b := c.blocks[i]
|
||||
_, ok := b.Rollup.Txs[txHash]
|
||||
if !ok {
|
||||
_, ok = b.Auction.Txs[txHash]
|
||||
}
|
||||
if ok {
|
||||
return &types.Receipt{
|
||||
TxHash: txHash,
|
||||
Status: types.ReceiptStatusSuccessful,
|
||||
BlockHash: b.Eth.Hash,
|
||||
BlockNumber: big.NewInt(b.Eth.BlockNum),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// func newHeader(number *big.Int) *types.Header {
|
||||
// return &types.Header{
|
||||
// Number: number,
|
||||
@@ -499,6 +539,14 @@ func (c *Client) EthBlockByNumber(ctx context.Context, blockNum int64) (*common.
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EthAddress returns the ethereum address of the account loaded into the Client
|
||||
func (c *Client) EthAddress() (*ethCommon.Address, error) {
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
return c.addr, nil
|
||||
}
|
||||
|
||||
var errTODO = fmt.Errorf("TODO: Not implemented yet")
|
||||
|
||||
//
|
||||
@@ -533,7 +581,7 @@ type transactionData struct {
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (c *Client) newTransaction(name string, value interface{}) *types.Transaction {
|
||||
func newTransaction(name string, value interface{}) *types.Transaction {
|
||||
data, err := json.Marshal(transactionData{name, value})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -548,9 +596,12 @@ func (c *Client) RollupForgeBatch(args *eth.RollupForgeBatchArgs) (tx *types.Tra
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
a := c.nextBlock().Auction
|
||||
ok, err := a.canForge(c.addr, a.Eth.BlockNum)
|
||||
ok, err := a.canForge(*c.addr, a.Eth.BlockNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -561,11 +612,15 @@ func (c *Client) RollupForgeBatch(args *eth.RollupForgeBatchArgs) (tx *types.Tra
|
||||
// TODO: Verify proof
|
||||
|
||||
// Auction
|
||||
err = a.forge(c.addr)
|
||||
err = a.forge(*c.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: If successful, store the tx in a successful array.
|
||||
// TODO: If failed, store the tx in a failed array.
|
||||
// TODO: Add method to move the tx to another block, reapply it there, and possibly go from successful to failed.
|
||||
|
||||
return c.addBatch(args)
|
||||
}
|
||||
|
||||
@@ -595,7 +650,7 @@ func (c *Client) addBatch(args *eth.RollupForgeBatchArgs) (*types.Transaction, e
|
||||
r.State.MapL1TxQueue[r.State.LastToForgeL1TxsNum] = eth.NewQueueStruct()
|
||||
}
|
||||
}
|
||||
ethTx := c.newTransaction("forgebatch", args)
|
||||
ethTx := r.addTransaction(newTransaction("forgebatch", args))
|
||||
c.forgeBatchArgsPending[ethTx.Hash()] = args
|
||||
r.Events.ForgeBatch = append(r.Events.ForgeBatch, eth.RollupEventForgeBatch{
|
||||
BatchNum: int64(len(r.State.ExitRoots)),
|
||||
@@ -611,6 +666,9 @@ func (c *Client) RollupAddToken(tokenAddress ethCommon.Address) (tx *types.Trans
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
nextBlock := c.nextBlock()
|
||||
r := nextBlock.Rollup
|
||||
@@ -622,7 +680,7 @@ func (c *Client) RollupAddToken(tokenAddress ethCommon.Address) (tx *types.Trans
|
||||
r.State.TokenList = append(r.State.TokenList, tokenAddress)
|
||||
r.Events.AddToken = append(r.Events.AddToken, eth.RollupEventAddToken{Address: tokenAddress,
|
||||
TokenID: uint32(len(r.State.TokenList) - 1)})
|
||||
return c.newTransaction("addtoken", tokenAddress), nil
|
||||
return r.addTransaction(newTransaction("addtoken", tokenAddress)), nil
|
||||
}
|
||||
|
||||
// RollupWithdrawSNARK is the interface to call the smart contract function
|
||||
@@ -636,6 +694,9 @@ func (c *Client) RollupWithdraw(tokenID int64, balance *big.Int, babyPubKey *bab
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -647,6 +708,9 @@ func (c *Client) RollupForceExit(fromIdx int64, amountF common.Float16, tokenID
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -658,6 +722,9 @@ func (c *Client) RollupForceTransfer(fromIdx int64, amountF common.Float16, toke
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -669,6 +736,9 @@ func (c *Client) RollupCreateAccountDepositTransfer(babyPubKey babyjub.PublicKey
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -689,6 +759,9 @@ func (c *Client) RollupDepositTransfer(fromIdx int64, loadAmountF, amountF commo
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -700,6 +773,9 @@ func (c *Client) RollupDeposit(fromIdx int64, loadAmountF common.Float16, tokenI
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -711,6 +787,9 @@ func (c *Client) RollupCreateAccountDepositFromRelayer(accountCreationAuthSig []
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -722,6 +801,9 @@ func (c *Client) RollupCreateAccountDeposit(babyPubKey babyjub.PublicKey, loadAm
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -760,6 +842,9 @@ func (c *Client) RollupUpdateForgeL1L2BatchTimeout(newForgeL1Timeout int64) (tx
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -771,6 +856,9 @@ func (c *Client) RollupUpdateFeeAddToken(newFeeAddToken *big.Int) (tx *types.Tra
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -835,6 +923,9 @@ func (c *Client) AuctionSetSlotDeadline(newDeadline uint8) (tx *types.Transactio
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -855,6 +946,9 @@ func (c *Client) AuctionSetOpenAuctionSlots(newOpenAuctionSlots uint16) (tx *typ
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -875,6 +969,9 @@ func (c *Client) AuctionSetClosedAuctionSlots(newClosedAuctionSlots uint16) (tx
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -895,6 +992,9 @@ func (c *Client) AuctionSetOutbidding(newOutbidding uint16) (tx *types.Transacti
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -915,6 +1015,9 @@ func (c *Client) AuctionSetAllocationRatio(newAllocationRatio [3]uint16) (tx *ty
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -935,6 +1038,9 @@ func (c *Client) AuctionSetDonationAddress(newDonationAddress ethCommon.Address)
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -955,6 +1061,9 @@ func (c *Client) AuctionSetBootCoordinator(newBootCoordinator ethCommon.Address)
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -977,6 +1086,9 @@ func (c *Client) AuctionChangeDefaultSlotSetBid(slotSet int64, newInitialMinBid
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -988,6 +1100,9 @@ func (c *Client) AuctionRegisterCoordinator(forgerAddress ethCommon.Address, URL
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
nextBlock := c.nextBlock()
|
||||
a := nextBlock.Auction
|
||||
@@ -996,14 +1111,14 @@ func (c *Client) AuctionRegisterCoordinator(forgerAddress ethCommon.Address, URL
|
||||
return nil, fmt.Errorf("Already registered")
|
||||
}
|
||||
a.State.Coordinators[forgerAddress] = ð.Coordinator{
|
||||
WithdrawalAddress: c.addr,
|
||||
WithdrawalAddress: *c.addr,
|
||||
URL: URL,
|
||||
}
|
||||
|
||||
a.Events.NewCoordinator = append(a.Events.NewCoordinator,
|
||||
eth.AuctionEventNewCoordinator{
|
||||
ForgerAddress: forgerAddress,
|
||||
WithdrawalAddress: c.addr,
|
||||
WithdrawalAddress: *c.addr,
|
||||
CoordinatorURL: URL,
|
||||
})
|
||||
|
||||
@@ -1011,7 +1126,7 @@ func (c *Client) AuctionRegisterCoordinator(forgerAddress ethCommon.Address, URL
|
||||
ForgerAddress ethCommon.Address
|
||||
URL string
|
||||
}
|
||||
return c.newTransaction("registercoordinator", data{forgerAddress, URL}), nil
|
||||
return a.addTransaction(newTransaction("registercoordinator", data{forgerAddress, URL})), nil
|
||||
}
|
||||
|
||||
// AuctionIsRegisteredCoordinator is the interface to call the smart contract function
|
||||
@@ -1029,11 +1144,24 @@ func (c *Client) AuctionUpdateCoordinatorInfo(forgerAddress ethCommon.Address, n
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
}
|
||||
|
||||
// AuctionGetSlotNumber is the interface to call the smart contract function
|
||||
func (c *Client) AuctionGetSlotNumber(blockNum int64) (int64, error) {
|
||||
c.rw.RLock()
|
||||
defer c.rw.RUnlock()
|
||||
|
||||
currentBlock := c.currentBlock()
|
||||
a := currentBlock.Auction
|
||||
return a.getSlotNumber(blockNum), nil
|
||||
}
|
||||
|
||||
// AuctionGetCurrentSlotNumber is the interface to call the smart contract function
|
||||
func (c *Client) AuctionGetCurrentSlotNumber() (int64, error) {
|
||||
c.rw.RLock()
|
||||
@@ -1070,15 +1198,6 @@ func (c *Client) AuctionGetSlotSet(slot int64) (*big.Int, error) {
|
||||
return nil, errTODO
|
||||
}
|
||||
|
||||
// AuctionGetSlotNumber is the interface to call the smart contract function
|
||||
func (c *Client) AuctionGetSlotNumber(blockNum int64) (*big.Int, error) {
|
||||
c.rw.RLock()
|
||||
defer c.rw.RUnlock()
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
}
|
||||
|
||||
// AuctionTokensReceived is the interface to call the smart contract function
|
||||
// func (c *Client) AuctionTokensReceived(operator, from, to ethCommon.Address, amount *big.Int, userData, operatorData []byte) error {
|
||||
// return errTODO
|
||||
@@ -1090,6 +1209,9 @@ func (c *Client) AuctionBid(slot int64, bidAmount *big.Int, forger ethCommon.Add
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { func() { c.revertIfErr(err, cpy) }() }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
nextBlock := c.nextBlock()
|
||||
a := nextBlock.Auction
|
||||
@@ -1130,7 +1252,7 @@ func (c *Client) AuctionBid(slot int64, bidAmount *big.Int, forger ethCommon.Add
|
||||
BidAmount *big.Int
|
||||
Forger ethCommon.Address
|
||||
}
|
||||
return c.newTransaction("bid", data{slot, bidAmount, forger}), nil
|
||||
return a.addTransaction(newTransaction("bid", data{slot, bidAmount, forger})), nil
|
||||
}
|
||||
|
||||
// AuctionMultiBid is the interface to call the smart contract function
|
||||
@@ -1139,6 +1261,9 @@ func (c *Client) AuctionMultiBid(startingSlot int64, endingSlot int64, slotSet [
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
@@ -1165,6 +1290,9 @@ func (c *Client) AuctionClaimHEZ(claimAddress ethCommon.Address) (tx *types.Tran
|
||||
defer c.rw.Unlock()
|
||||
cpy := c.nextBlock().copy()
|
||||
defer func() { c.revertIfErr(err, cpy) }()
|
||||
if c.addr == nil {
|
||||
return nil, eth.ErrAccountNil
|
||||
}
|
||||
|
||||
log.Error("TODO")
|
||||
return nil, errTODO
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestClientInterface(t *testing.T) {
|
||||
var c eth.ClientInterface
|
||||
var timer timer
|
||||
clientSetup := NewClientSetupExample()
|
||||
client := NewClient(true, &timer, ethCommon.Address{}, clientSetup)
|
||||
client := NewClient(true, &timer, ðCommon.Address{}, clientSetup)
|
||||
c = client
|
||||
require.NotNil(t, c)
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func TestClientInterface(t *testing.T) {
|
||||
func TestClientEth(t *testing.T) {
|
||||
var timer timer
|
||||
clientSetup := NewClientSetupExample()
|
||||
c := NewClient(true, &timer, ethCommon.Address{}, clientSetup)
|
||||
c := NewClient(true, &timer, ðCommon.Address{}, clientSetup)
|
||||
blockNum, err := c.EthCurrentBlock()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, int64(0), blockNum)
|
||||
@@ -76,7 +76,7 @@ func TestClientAuction(t *testing.T) {
|
||||
clientSetup.AuctionVariables.DefaultSlotSetBid = [6]*big.Int{
|
||||
big.NewInt(1000), big.NewInt(1100), big.NewInt(1200),
|
||||
big.NewInt(1300), big.NewInt(1400), big.NewInt(1500)}
|
||||
c := NewClient(true, &timer, addrWithdraw, clientSetup)
|
||||
c := NewClient(true, &timer, &addrWithdraw, clientSetup)
|
||||
|
||||
// Check several cases in which bid doesn't succed, and also do 2 successful bids.
|
||||
|
||||
@@ -124,7 +124,7 @@ func TestClientRollup(t *testing.T) {
|
||||
|
||||
var timer timer
|
||||
clientSetup := NewClientSetupExample()
|
||||
c := NewClient(true, &timer, ethCommon.Address{}, clientSetup)
|
||||
c := NewClient(true, &timer, ðCommon.Address{}, clientSetup)
|
||||
|
||||
// Add a token
|
||||
|
||||
|
||||
Reference in New Issue
Block a user