From cb1b820256744eb6c7a43df3f036ea7c7f209300 Mon Sep 17 00:00:00 2001 From: a_bennassar Date: Thu, 13 Aug 2020 12:53:42 +0200 Subject: [PATCH] Update SQL schemas --- batchbuilder/batchbuilder.go | 10 +- common/l1tx.go | 26 ++-- common/l2tx.go | 17 ++- common/pooll2tx.go | 67 +++++----- common/tx.go | 13 -- db/historydb/migrations/001_init.sql | 20 ++- db/l2db/l2db.go | 89 ++++++++++--- db/l2db/l2db_test.go | 181 +++++++++++++++++++++++++++ db/l2db/migrations/001_init.sql | 37 ++++++ db/utils.go | 40 ++++++ go.mod | 1 + go.sum | 25 +--- 12 files changed, 418 insertions(+), 108 deletions(-) create mode 100644 db/l2db/l2db_test.go create mode 100644 db/l2db/migrations/001_init.sql diff --git a/batchbuilder/batchbuilder.go b/batchbuilder/batchbuilder.go index c4feca1..ec71638 100644 --- a/batchbuilder/batchbuilder.go +++ b/batchbuilder/batchbuilder.go @@ -88,7 +88,7 @@ func (bb *BatchBuilder) BuildBatch(configBatch ConfigBatch, l1usertxs, l1coordin case common.TxTypeTransfer: // go to the MT account of sender and receiver, and update // balance & nonce - err := bb.applyTransfer(tx.Tx) + err := bb.applyTransfer(tx.Tx()) if err != nil { return nil, err } @@ -106,7 +106,7 @@ func (bb *BatchBuilder) processL1Tx(tx common.L1Tx) error { case common.TxTypeForceTransfer, common.TxTypeTransfer: // go to the MT account of sender and receiver, and update balance // & nonce - err := bb.applyTransfer(tx.Tx) + err := bb.applyTransfer(tx.Tx()) if err != nil { return err } @@ -136,7 +136,7 @@ func (bb *BatchBuilder) processL1Tx(tx common.L1Tx) error { if err != nil { return err } - err = bb.applyTransfer(tx.Tx) + err = bb.applyTransfer(tx.Tx()) if err != nil { return err } @@ -186,9 +186,9 @@ func (bb *BatchBuilder) applyDeposit(tx common.L1Tx, transfer bool) error { return err } // substract amount to the sender - accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Tx.Amount) + accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount) // add amount to the receiver - accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Tx.Amount) + accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) // update receiver account in localStateDB err = bb.localStateDB.UpdateAccount(tx.ToIdx, accReceiver) if err != nil { diff --git a/common/l1tx.go b/common/l1tx.go index 3324bb8..c64219b 100644 --- a/common/l1tx.go +++ b/common/l1tx.go @@ -9,15 +9,19 @@ import ( // L1Tx is a struct that represents a L1 tx type L1Tx struct { - Tx - UserOrigin bool // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes - PublicKey babyjub.PublicKey - LoadAmount *big.Int // amount transfered from L1 -> L2 - EthBlockNum uint64 // Ethereum Block Number in which this L1Tx was added to the queue - EthTxHash eth.Hash // TxHash that added this L1Tx to the queue - Position int // Position among all the L1Txs in that batch - ToForgeL1TxsNum uint32 // toForgeL1TxsNum in which the tx was forged / will be forged - FromBJJ babyjub.PublicKey - CreateAccount bool // "from" + token ID is a new account - FromEthAddr eth.Address + // Stored in DB: mandatory fileds + TxID TxID `meddler:"tx_id"` + ToForgeL1TxsNum uint32 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged + Position int `meddler:"position"` + UserOrigin bool `meddler:"user_origin"` // true if the tx was originated by a user, false if it was aoriginated by a coordinator. Note that this differ from the spec for implementation simplification purpposes + FromIdx Idx `meddler:"from_idx"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) + FromEthAddr eth.Address `meddler:"from_eth_addr"` + FromBJJ *babyjub.PublicKey `meddler:"from_bjj"` + ToIdx Idx `meddler:"to_idx"` // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer + TokenID TokenID `meddler:"token_id"` + Amount *big.Int `meddler:"amount,bigint"` + LoadAmount *big.Int `meddler:"load_amount,bigint"` + EthBlockNum uint64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue + // Extra metadata, may be uninitialized + Type TxType `meddler:"-"` // optional, descrives which kind of tx it's } diff --git a/common/l2tx.go b/common/l2tx.go index 127b152..47ad093 100644 --- a/common/l2tx.go +++ b/common/l2tx.go @@ -1,7 +1,20 @@ package common +import ( + "math/big" +) + // L2Tx is a struct that represents an already forged L2 tx type L2Tx struct { - Tx - Position int // Position among all the L1Txs in that batch + // Stored in DB: mandatory fileds + TxID TxID `meddler:"tx_id"` + BatchNum BatchNum `meddler:"batch_num"` // batchNum in which this tx was forged. + Position int `meddler:"position"` + FromIdx Idx `meddler:"from_idx"` + ToIdx Idx `meddler:"to_idx"` + Amount *big.Int `meddler:"amount,bigint"` + Fee FeeSelector `meddler:"fee"` + Nonce uint64 `meddler:"nonce"` + // Extra metadata, may be uninitialized + Type TxType `meddler:"-"` // optional, descrives which kind of tx it's } diff --git a/common/pooll2tx.go b/common/pooll2tx.go index fb943ed..9becf07 100644 --- a/common/pooll2tx.go +++ b/common/pooll2tx.go @@ -10,37 +10,46 @@ import ( // PoolL2Tx is a struct that represents a L2Tx sent by an account to the coordinator hat is waiting to be forged type PoolL2Tx struct { - Tx - ToBJJ babyjub.PublicKey - Status PoolL2TxStatus - RqTxCompressedData []byte // 253 bits, optional for atomic txs - RqTx RqTx - Timestamp time.Time // time when added to the tx pool - Signature babyjub.Signature // tx signature - ToEthAddr eth.Address - AbsoluteFee float64 // TODO add methods to calculate this value from Tx.Fee tables + priceupdater tables + // Stored in DB: mandatory fileds + TxID TxID `meddler:"tx_id"` + FromIdx Idx `meddler:"from_idx"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) + ToIdx Idx `meddler:"to_idx"` // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer + ToEthAddr eth.Address `meddler:"to_eth_addr"` + ToBJJ *babyjub.PublicKey `meddler:"to_bjj"` // TODO: stop using json, use scanner/valuer + TokenID TokenID `meddler:"token_id"` + Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16 + Fee FeeSelector `meddler:"fee"` + Nonce uint64 `meddler:"nonce"` // effective 48 bits used + State PoolL2TxState `meddler:"state"` + Signature babyjub.Signature `meddler:"signature"` // tx signature + Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool + // Stored in DB: optional fileds, may be uninitialized + BatchNum BatchNum `meddler:"batch_num,zeroisnull"` // batchNum in which this tx was forged. Presence indicates "forged" state. + RqFromIdx Idx `meddler:"rq_from_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) + RqToIdx Idx `meddler:"rq_to_idx,zeroisnull"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) + RqToEthAddr eth.Address `meddler:"rq_to_eth_addr"` + RqToBJJ *babyjub.PublicKey `meddler:"rq_to_bjj"` // TODO: stop using json, use scanner/valuer + RqTokenID TokenID `meddler:"rq_token_id,zeroisnull"` + RqAmount *big.Int `meddler:"rq_amount,bigintnull"` // TODO: change to float16 + RqFee FeeSelector `meddler:"rq_fee,zeroisnull"` + RqNonce uint64 `meddler:"rq_nonce,zeroisnull"` // effective 48 bits used + AbsoluteFee float64 `meddler:"absolute_fee,zeroisnull"` + AbsoluteFeeUpdate time.Time `meddler:"absolute_fee_update,utctimez"` + // Extra metadata, may be uninitialized + Type TxType `meddler:"-"` // optional, descrives which kind of tx it's + RqTxCompressedData []byte `meddler:"-"` // 253 bits, optional for atomic txs } -// RqTx Transaction Data used to indicate that a transaction depends on another transaction -type RqTx struct { - FromEthAddr eth.Address - ToEthAddr eth.Address - TokenID TokenID - Amount *big.Int - FeeSelector FeeSelector - Nonce uint64 // effective 48 bits used -} - -// PoolL2TxStatus is a struct that represents the status of a L2 transaction -type PoolL2TxStatus string +// PoolL2TxState is a struct that represents the status of a L2 transaction +type PoolL2TxState string const ( - // PoolL2TxStatusPending represents a valid L2Tx that hasn't started the forging process - PoolL2TxStatusPending PoolL2TxStatus = "Pending" - // PoolL2TxStatusForging represents a valid L2Tx that has started the forging process - PoolL2TxStatusForging PoolL2TxStatus = "Forging" - // PoolL2TxStatusForged represents a L2Tx that has already been forged - PoolL2TxStatusForged PoolL2TxStatus = "Forged" - // PoolL2TxStatusInvalid represents a L2Tx that has been invalidated - PoolL2TxStatusInvalid PoolL2TxStatus = "Invalid" + // PoolL2TxStatePending represents a valid L2Tx that hasn't started the forging process + PoolL2TxStatePending PoolL2TxState = "pend" + // PoolL2TxStateForging represents a valid L2Tx that has started the forging process + PoolL2TxStateForging PoolL2TxState = "fing" + // PoolL2TxStateForged represents a L2Tx that has already been forged + PoolL2TxStateForged PoolL2TxState = "fged" + // PoolL2TxStateInvalid represents a L2Tx that has been invalidated + PoolL2TxStateInvalid PoolL2TxState = "invl" ) diff --git a/common/tx.go b/common/tx.go index 3f2156f..fc0938c 100644 --- a/common/tx.go +++ b/common/tx.go @@ -28,19 +28,6 @@ func IdxFromBigInt(b *big.Int) (Idx, error) { return Idx(uint32(b.Int64())), nil } -// Tx is a struct that represents a Hermez network transaction -type Tx struct { - TxID TxID - FromIdx Idx // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit) - ToIdx Idx // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer - TokenID TokenID - Amount *big.Int - Nonce uint64 // effective 48 bits used - Fee FeeSelector - Type TxType // optional, descrives which kind of tx it's - BatchNum BatchNum // batchNum in which this tx was forged. Presence indicates "forged" state. -} - // TxID is the identifier of a Hermez network transaction type TxID Hash // Hash is a guess diff --git a/db/historydb/migrations/001_init.sql b/db/historydb/migrations/001_init.sql index b2b2005..ee1fcba 100644 --- a/db/historydb/migrations/001_init.sql +++ b/db/historydb/migrations/001_init.sql @@ -10,7 +10,7 @@ CREATE TABLE slot_min_prices ( min_prices VARCHAR(200) NOT NULL ); -CREATE TABLE coordiantor ( +CREATE TABLE coordianator ( forger_addr BYTEA NOT NULL, eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE, beneficiary_addr BYTEA NOT NULL, @@ -59,19 +59,17 @@ CREATE TABLE token ( CREATE TABLE l1tx ( tx_id BYTEA PRIMARY KEY, - from_idx BIGINT NOT NULL, - to_idx BIGINT NOT NULL, - token_id INT NOT NULL REFERENCES token (token_id), - amount NUMERIC NOT NULL, - nonce BIGINT NOT NULL, - fee INT NOT NULL, - eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE, to_forge_l1_txs_num BIGINT NOT NULL, position INT NOT NULL, - origin_user BOOLEAN NOT NULL, + user_origin BOOLEAN NOT NULL, + from_idx BIGINT NOT NULL, from_eth_addr BYTEA NOT NULL, from_bjj BYTEA NOT NULL, - load_amount BYTEA NOT NULL + to_idx BIGINT NOT NULL, + token_id INT NOT NULL REFERENCES token (token_id), + amount NUMERIC NOT NULL, + load_amount BYTEA NOT NULL, + eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE ); CREATE TABLE l2tx ( @@ -101,6 +99,6 @@ DROP TABLE token; DROP TABLE bid; DROP TABLE exit_tree; DROP TABLE batch; -DROP TABLE coordiantor; +DROP TABLE coordianator; DROP TABLE slot_min_prices; DROP TABLE block; \ No newline at end of file diff --git a/db/l2db/l2db.go b/db/l2db/l2db.go index c679648..9991437 100644 --- a/db/l2db/l2db.go +++ b/db/l2db/l2db.go @@ -1,17 +1,24 @@ package l2db import ( + "fmt" + "strconv" "time" eth "github.com/ethereum/go-ethereum/common" + "github.com/gobuffalo/packr/v2" "github.com/hermeznetwork/hermez-node/common" - "github.com/jinzhu/gorm" + "github.com/hermeznetwork/hermez-node/db" + "github.com/jmoiron/sqlx" + _ "github.com/lib/pq" // driver for postgres DB + migrate "github.com/rubenv/sql-migrate" + "github.com/russross/meddler" ) // L2DB stores L2 txs and authorization registers received by the coordinator and keeps them until they are no longer relevant // due to them being forged or invalid after a safety period type L2DB struct { - db *gorm.DB + db *sqlx.DB safetyPeriod uint16 ttl time.Duration maxTxs uint32 @@ -25,23 +32,28 @@ type L2DB struct { // (to prevent tx that won't ever be forged to stay there, will be used if maxTxs is exceeded). // autoPurgePeriod will be used as delay between calls to Purge. If the value is 0, it will be disabled. func NewL2DB( - dbDialect, dbArgs string, + port int, host, user, password, dbname string, safetyPeriod uint16, maxTxs uint32, TTL time.Duration, ) (*L2DB, error) { + // init meddler + db.InitMeddler() + meddler.Default = meddler.PostgreSQL // Stablish DB connection - db, err := gorm.Open(dbDialect, dbArgs) + psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname) + db, err := sqlx.Connect("postgres", psqlconn) if err != nil { return nil, err } - // Create or update SQL schemas - // WARNING: AutoMigrate will ONLY create tables, missing columns and missing indexes, - // and WON’T change existing column’s type or delete unused columns to protect your data. - // more info: http://gorm.io/docs/migration.html - db.AutoMigrate(&common.PoolL2Tx{}) - // TODO: db.AutoMigrate(&common.RegisterAuthorization{}) + // Run DB migrations + migrations := &migrate.PackrMigrationSource{ + Box: packr.New("history-migrations", "./migrations"), + } + if _, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up); err != nil { + return nil, err + } return &L2DB{ db: db, @@ -53,38 +65,66 @@ func NewL2DB( // AddTx inserts a tx into the L2DB func (l2db *L2DB) AddTx(tx *common.PoolL2Tx) error { - return nil + return meddler.Insert(l2db.db, "tx_pool", tx) } // AddAccountCreationAuth inserts an account creation authorization into the DB -func (l2db *L2DB) AddAccountCreationAuth(auth *common.AccountCreationAuth) error { // TODO: AddRegisterAuthorization(auth &common.RegisterAuthorization) +func (l2db *L2DB) AddAccountCreationAuth(auth *common.AccountCreationAuth) error { + // TODO: impl return nil } // GetTx return the specified Tx func (l2db *L2DB) GetTx(txID common.TxID) (*common.PoolL2Tx, error) { - return nil, nil + tx := new(common.PoolL2Tx) + return tx, meddler.QueryRow( + l2db.db, tx, + "SELECT * FROM tx_pool WHERE tx_id = $1;", + txID, + ) } // GetPendingTxs return all the pending txs of the L2DB -func (l2db *L2DB) GetPendingTxs() ([]common.PoolL2Tx, error) { - return nil, nil +func (l2db *L2DB) GetPendingTxs() ([]*common.PoolL2Tx, error) { + var txs []*common.PoolL2Tx + err := meddler.QueryAll( + l2db.db, &txs, + "SELECT * FROM tx_pool WHERE state = $1", + common.PoolL2TxStatePending, + ) + return txs, err } // GetAccountCreationAuth return the authorization to make registers of an Ethereum address func (l2db *L2DB) GetAccountCreationAuth(ethAddr eth.Address) (*common.AccountCreationAuth, error) { + // TODO: impl return nil, nil } // StartForging updates the state of the transactions that will begin the forging process. // The state of the txs referenced by txIDs will be changed from Pending -> Forging -func (l2db *L2DB) StartForging(txIDs []common.TxID) error { - return nil +func (l2db *L2DB) StartForging(txIDs []common.TxID, batchNum common.BatchNum) error { + query, args, err := sqlx.In( + `UPDATE tx_pool + SET state = ?, batch_num = ? + WHERE state = ? AND tx_id IN (?);`, + string(common.PoolL2TxStateForging), + strconv.Itoa(int(batchNum)), + string(common.PoolL2TxStatePending), + txIDs, + ) + if err != nil { + return err + } + query = l2db.db.Rebind(query) + _, err = l2db.db.Exec(query, args...) + return err } // DoneForging updates the state of the transactions that have been forged // so the state of the txs referenced by txIDs will be changed from Forging -> Forged func (l2db *L2DB) DoneForging(txIDs []common.TxID) error { + // TODO: impl return nil } @@ -97,18 +137,33 @@ func (l2db *L2DB) InvalidateTxs(txIDs []common.TxID) error { // CheckNonces invalidate txs with nonces that are smaller than their respective accounts nonces. // The state of the affected txs will be changed from Pending -> Invalid func (l2db *L2DB) CheckNonces(updatedAccounts []common.Account) error { + // TODO: impl + return nil +} + +// GetTxsByAbsoluteFeeUpdate return the txs that have an AbsoluteFee updated before olderThan +func (l2db *L2DB) GetTxsByAbsoluteFeeUpdate(olderThan time.Time) ([]*common.PoolL2Tx, error) { + // TODO: impl + return nil, nil +} + +// UpdateTxs update existing txs from the pool (TxID must exist) +func (l2db *L2DB) UpdateTxs(txs []*common.PoolL2Tx) error { + // TODO: impl return nil } // Reorg updates the state of txs that were updated in a batch that has been discarted due to a blockchian reorg. // The state of the affected txs can change form Forged -> Pending or from Invalid -> Pending func (l2db *L2DB) Reorg(lastValidBatch common.BatchNum) error { + // TODO: impl return nil } // Purge deletes transactions that have been forged or marked as invalid for longer than the safety period // it also deletes txs that has been in the L2DB for longer than the ttl if maxTxs has been exceeded func (l2db *L2DB) Purge() error { + // TODO: impl return nil } diff --git a/db/l2db/l2db_test.go b/db/l2db/l2db_test.go new file mode 100644 index 0000000..1a80d2a --- /dev/null +++ b/db/l2db/l2db_test.go @@ -0,0 +1,181 @@ +package l2db + +import ( + "fmt" + "math/big" + "os" + "strconv" + "testing" + "time" + + eth "github.com/ethereum/go-ethereum/common" + "github.com/hermeznetwork/hermez-node/common" + "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/stretchr/testify/assert" +) + +var l2DB *L2DB + +// In order to run the test you need to run a Posgres DB with +// a database named "l2" that is accessible by +// user: "hermez" +// pass: set it using the env var POSTGRES_PASS +// This can be achieved by running: POSTGRES_PASS=your_strong_pass && sudo docker run --rm --name hermez-db-test -p 5432:5432 -e POSTGRES_DB=history -e POSTGRES_USER=hermez -e POSTGRES_PASSWORD=$POSTGRES_PASS -d postgres && sleep 2s && sudo docker exec -it hermez-db-test psql -a history -U hermez -c "CREATE DATABASE l2;" +// After running the test you can stop the container by running: sudo docker kill hermez-ydb-test +// If you already did that for the HistoryDB you don't have to do it again +func TestMain(m *testing.M) { + // init DB + var err error + pass := os.Getenv("POSTGRES_PASS") + l2DB, err = NewL2DB(5432, "localhost", "hermez", pass, "l2", 10, 512, 24*time.Hour) + if err != nil { + panic(err) + } + // Run tests + result := m.Run() + // Close DB + if err := l2DB.Close(); err != nil { + fmt.Println("Error closing the history DB:", err) + } + os.Exit(result) +} + +func TestAddTx(t *testing.T) { + const nInserts = 20 + cleanDB() + txs := genTxs(nInserts) + for _, tx := range txs { + err := l2DB.AddTx(tx) + assert.NoError(t, err) + fetchedTx, err := l2DB.GetTx(tx.TxID) + assert.NoError(t, err) + assert.Equal(t, tx.Timestamp.Unix(), fetchedTx.Timestamp.Unix()) + tx.Timestamp = fetchedTx.Timestamp + assert.Equal(t, tx.AbsoluteFeeUpdate.Unix(), fetchedTx.AbsoluteFeeUpdate.Unix()) + tx.Timestamp = fetchedTx.Timestamp + tx.AbsoluteFeeUpdate = fetchedTx.AbsoluteFeeUpdate + assert.Equal(t, tx, fetchedTx) + } +} + +func BenchmarkAddTx(b *testing.B) { + const nInserts = 20 + cleanDB() + txs := genTxs(nInserts) + now := time.Now() + for _, tx := range txs { + l2DB.AddTx(tx) + } + elapsedTime := time.Since(now) + fmt.Println("Time to insert 2048 txs:", elapsedTime) +} + +func TestGetPending(t *testing.T) { + const nInserts = 20 + cleanDB() + txs := genTxs(nInserts) + var pendingTxs []*common.PoolL2Tx + for _, tx := range txs { + err := l2DB.AddTx(tx) + assert.NoError(t, err) + if tx.State == common.PoolL2TxStatePending { + pendingTxs = append(pendingTxs, tx) + } + } + fetchedTxs, err := l2DB.GetPendingTxs() + assert.NoError(t, err) + assert.Equal(t, len(pendingTxs), len(fetchedTxs)) + for i, fetchedTx := range fetchedTxs { + assert.Equal(t, pendingTxs[i].Timestamp.Unix(), fetchedTx.Timestamp.Unix()) + pendingTxs[i].Timestamp = fetchedTx.Timestamp + assert.Equal(t, pendingTxs[i].AbsoluteFeeUpdate.Unix(), fetchedTx.AbsoluteFeeUpdate.Unix()) + pendingTxs[i].AbsoluteFeeUpdate = fetchedTx.AbsoluteFeeUpdate + assert.Equal(t, pendingTxs[i], fetchedTx) + } +} + +func TestStartForging(t *testing.T) { + const nInserts = 24 + const fakeBlockNum = 33 + cleanDB() + txs := genTxs(nInserts) + var startForgingTxs []*common.PoolL2Tx + var startForgingTxIDs []common.TxID + randomizer := 0 + for _, tx := range txs { + err := l2DB.AddTx(tx) + assert.NoError(t, err) + if tx.State == common.PoolL2TxStatePending && randomizer%2 == 0 { + randomizer++ + startForgingTxs = append(startForgingTxs, tx) + startForgingTxIDs = append(startForgingTxIDs, tx.TxID) + } + if tx.State == common.PoolL2TxStateForging { + startForgingTxs = append(startForgingTxs, tx) + } + } + err := l2DB.StartForging(startForgingTxIDs, fakeBlockNum) + assert.NoError(t, err) + // TODO: Fetch txs and check that they've been updated correctly +} + +func genTxs(n int) []*common.PoolL2Tx { + // WARNING: This tx doesn't follow the protocol (signature, txID, ...) + // it's just to test geting/seting from/to the DB. + // Type and RqTxCompressedData: not initialized because it's not stored + // on the DB and add noise when checking results. + txs := make([]*common.PoolL2Tx, 0, n) + privK := babyjub.NewRandPrivKey() + for i := 0; i < n; i++ { + var state common.PoolL2TxState + if i%4 == 0 { + state = common.PoolL2TxStatePending + } else if i%4 == 1 { + state = common.PoolL2TxStateInvalid + } else if i%4 == 2 { + state = common.PoolL2TxStateForging + } else if i%4 == 3 { + state = common.PoolL2TxStateForged + } + tx := &common.PoolL2Tx{ + TxID: common.TxID(common.Hash([]byte(strconv.Itoa(i)))), + FromIdx: 47, + ToIdx: 96, + ToEthAddr: eth.BigToAddress(big.NewInt(234523534)), + ToBJJ: privK.Public(), + TokenID: 73, + Amount: big.NewInt(3487762374627846747), + Fee: 99, + Nonce: 28, + State: state, + Signature: *privK.SignPoseidon(big.NewInt(674238462)), + Timestamp: time.Now().UTC(), + } + if i%2 == 0 { // Optional parameters: rq + tx.RqFromIdx = 893 + tx.RqToIdx = 334 + tx.RqToEthAddr = eth.BigToAddress(big.NewInt(239457111187)) + tx.RqToBJJ = privK.Public() + tx.RqTokenID = 222 + tx.RqAmount = big.NewInt(3487762374627846747) + tx.RqFee = 11 + tx.RqNonce = 78 + } + if i%3 == 0 { // Optional parameters: things that get updated "a posteriori" + tx.BatchNum = 489 + tx.AbsoluteFee = 39.12345 + tx.AbsoluteFeeUpdate = time.Now().UTC() + } + txs = append(txs, tx) + } + return txs +} + +func cleanDB() { + if _, err := l2DB.db.Exec("DELETE FROM tx_pool"); err != nil { + panic(err) + } + if _, err := l2DB.db.Exec("DELETE FROM account_creation_auth"); err != nil { + panic(err) + } +} diff --git a/db/l2db/migrations/001_init.sql b/db/l2db/migrations/001_init.sql new file mode 100644 index 0000000..2e6dab1 --- /dev/null +++ b/db/l2db/migrations/001_init.sql @@ -0,0 +1,37 @@ +-- +migrate Up +CREATE TABLE tx_pool ( + tx_id BYTEA PRIMARY KEY, + from_idx BIGINT NOT NULL, + to_idx BIGINT NOT NULL, + to_eth_addr BYTEA NOT NULL, + to_bjj BYTEA NOT NULL, + token_id INT NOT NULL, + amount BYTEA NOT NULL, + fee SMALLINT NOT NULL, + nonce BIGINT NOT NULL, + state CHAR(4) NOT NULL, + batch_num BIGINT, + rq_from_idx BIGINT, + rq_to_idx BIGINT, + rq_to_eth_addr BYTEA, + rq_to_bjj BYTEA, + rq_token_id INT, + rq_amount BYTEA, + rq_fee SMALLINT, + rq_nonce BIGINT, + signature BYTEA NOT NULL, + timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL, + absolute_fee NUMERIC, + absolute_fee_update TIMESTAMP WITHOUT TIME ZONE +); + +CREATE TABLE account_creation_auth ( + eth_addr BYTEA PRIMARY KEY, + bjj BYTEA NOT NULL, + account_creation_auth_sig BYTEA NOT NULL, + timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL +); + +-- +migrate Down +DROP TABLE account_creation_auth; +DROP TABLE tx_pool; \ No newline at end of file diff --git a/db/utils.go b/db/utils.go index 6eb60a7..6b4df34 100644 --- a/db/utils.go +++ b/db/utils.go @@ -13,6 +13,7 @@ import ( // InitMeddler registers tags to be used to read/write from SQL DBs using meddler func InitMeddler() { meddler.Register("bigint", BigIntMeddler{}) + meddler.Register("bigintnull", BigIntNullMeddler{}) } // BulkInsert performs a bulk insert with a single statement into the specified table. Example: @@ -76,3 +77,42 @@ func (b BigIntMeddler) PreWrite(fieldPtr interface{}) (saveValue interface{}, er return str, nil } + +// BigIntNullMeddler encodes or decodes the field value to or from JSON +type BigIntNullMeddler struct{} + +// PreRead is called before a Scan operation for fields that have the BigIntNullMeddler +func (b BigIntNullMeddler) PreRead(fieldAddr interface{}) (scanTarget interface{}, err error) { + return &fieldAddr, nil +} + +// PostRead is called after a Scan operation for fields that have the BigIntNullMeddler +func (b BigIntNullMeddler) PostRead(fieldPtr, scanTarget interface{}) error { + sv := reflect.ValueOf(scanTarget) + if sv.Elem().IsNil() { + // null column, so set target to be zero value + fv := reflect.ValueOf(fieldPtr) + fv.Elem().Set(reflect.Zero(fv.Elem().Type())) + return nil + } + // not null + encoded := new([]byte) + refEnc := reflect.ValueOf(encoded) + refEnc.Elem().Set(sv.Elem().Elem()) + data, err := base64.StdEncoding.DecodeString(string(*encoded)) + if err != nil { + return fmt.Errorf("big.Int decode error: %v", err) + } + field := fieldPtr.(**big.Int) + *field = new(big.Int).SetBytes(data) + return nil +} + +// PreWrite is called before an Insert or Update operation for fields that have the BigIntNullMeddler +func (b BigIntNullMeddler) PreWrite(fieldPtr interface{}) (saveValue interface{}, err error) { + field := fieldPtr.(*big.Int) + if field == nil { + return nil, nil + } + return base64.StdEncoding.EncodeToString(field.Bytes()), nil +} diff --git a/go.mod b/go.mod index 3e5e316..c62a87d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( github.com/dghubble/sling v1.3.0 github.com/ethereum/go-ethereum v1.9.17 + github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gobuffalo/packr/v2 v2.8.0 github.com/iden3/go-iden3-core v0.0.8 github.com/iden3/go-iden3-crypto v0.0.6-0.20200806115047-327a8175d6eb diff --git a/go.sum b/go.sum index 46f5d6d..fc56be6 100644 --- a/go.sum +++ b/go.sum @@ -19,7 +19,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= @@ -35,7 +34,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -111,8 +109,6 @@ github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzh github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= -github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dghubble/sling v1.3.0 h1:pZHjCJq4zJvc6qVQ5wN1jo5oNZlNE0+8T/h0XeXBUKU= github.com/dghubble/sling v1.3.0/go.mod h1:XXShWaBWKzNLhu2OxikSNFrlsvowtz4kyRuXUG7oQKY= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -134,8 +130,6 @@ github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3 github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/ethereum/go-ethereum v1.9.12/go.mod h1:PvsVkQmhZFx92Y+h2ylythYlheEDt/uBgFbl61Js/jo= github.com/ethereum/go-ethereum v1.9.13/go.mod h1:qwN9d1GLyDh0N7Ab8bMGd0H9knaji2jOBm2RrMGjXls= github.com/ethereum/go-ethereum v1.9.17 h1:2D02O8KcoyQHxfizvMi0vGXXzFIkQTMeKXwt0+4SYEA= @@ -270,12 +264,14 @@ github.com/iden3/go-iden3-core v0.0.8/go.mod h1:URNjIhMql6sEbWubIGrjJdw5wHCE1Pk1 github.com/iden3/go-iden3-crypto v0.0.5/go.mod h1:XKw1oDwYn2CIxKOtr7m/mL5jMn4mLOxAxtZBRxQBev8= github.com/iden3/go-iden3-crypto v0.0.6-0.20200723082457-29a66457f0bf h1:/7L5dEqctuzJY2g8OEQct+1Y+n2sMKyd4JoYhw2jy1s= github.com/iden3/go-iden3-crypto v0.0.6-0.20200723082457-29a66457f0bf/go.mod h1:XKw1oDwYn2CIxKOtr7m/mL5jMn4mLOxAxtZBRxQBev8= -github.com/iden3/go-iden3-crypto v0.0.6-0.20200806115047-327a8175d6eb h1:4Vqq5ZoqQd9t3Uj7MUbak7eHmtaYs8mqQoo8T1DS7tA= -github.com/iden3/go-iden3-crypto v0.0.6-0.20200806115047-327a8175d6eb/go.mod h1:XKw1oDwYn2CIxKOtr7m/mL5jMn4mLOxAxtZBRxQBev8= github.com/iden3/go-merkletree v0.0.0-20200723202738-75e24244a1e3 h1:QR6LqG1HqqPE4myiLR73QFIieAfwODG3bqo1juuaqVI= github.com/iden3/go-merkletree v0.0.0-20200723202738-75e24244a1e3/go.mod h1:Fc49UeywIsj8nUfb5lxBzmWrMeMmqzTJ5F0OcjdiEME= github.com/iden3/go-merkletree v0.0.0-20200806171216-dd600560e44c h1:EzVMSVkwKdfcOR1a+rZe9dfbtAj6KdJnBxOEZ5B+gCQ= github.com/iden3/go-merkletree v0.0.0-20200806171216-dd600560e44c/go.mod h1:Fc49UeywIsj8nUfb5lxBzmWrMeMmqzTJ5F0OcjdiEME= +github.com/iden3/go-iden3-crypto v0.0.6-0.20200813145745-66519124ca17 h1:aqy++85MX/ylQyKkuoK7zJ8hHEW5mT4mLL6pguo+X8Y= +github.com/iden3/go-iden3-crypto v0.0.6-0.20200813145745-66519124ca17/go.mod h1:oBgthFLboAWi9feaBUFy7OxEcyn9vA1khHSL/WwWFyg= +github.com/iden3/go-iden3-crypto v0.0.6-0.20200814110325-70841d78e7d8 h1:1QeVvr/DjiBpk8tw3vBfYD+zs0JeYl3fPIUA/foDq3w= +github.com/iden3/go-iden3-crypto v0.0.6-0.20200814110325-70841d78e7d8/go.mod h1:oBgthFLboAWi9feaBUFy7OxEcyn9vA1khHSL/WwWFyg= github.com/iden3/go-merkletree v0.0.0-20200807083900-f6f82d8375d5 h1:qvWSCt3AYxj65uTdW6lLSKlrbckcHghOAW4TwdfJ+N8= github.com/iden3/go-merkletree v0.0.0-20200807083900-f6f82d8375d5/go.mod h1:Fc49UeywIsj8nUfb5lxBzmWrMeMmqzTJ5F0OcjdiEME= github.com/iden3/go-merkletree v0.0.0-20200815105542-2277604e65dd h1:AkPlODLWkgQT9p1k6LnO3aRLIIeVL9ENof/YW87QL14= @@ -293,12 +289,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= -github.com/jinzhu/gorm v1.9.15 h1:OdR1qFvtXktlxk73XFYMiYn9ywzTwytqe4QkuMRqc38= -github.com/jinzhu/gorm v1.9.15/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= -github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= @@ -332,7 +322,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -362,7 +351,6 @@ github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -558,7 +546,6 @@ golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -583,7 +570,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -604,10 +590,9 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +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/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=