mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-06 19:06:42 +01:00
Update SQL schemas
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
13
common/tx.go
13
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
|
||||
|
||||
|
||||
@@ -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,
|
||||
to_forge_l1_txs_num BIGINT NOT NULL,
|
||||
position INT NOT NULL,
|
||||
user_origin BOOLEAN NOT NULL,
|
||||
from_idx BIGINT NOT NULL,
|
||||
from_eth_addr BYTEA NOT NULL,
|
||||
from_bjj BYTEA 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,
|
||||
from_eth_addr BYTEA NOT NULL,
|
||||
from_bjj BYTEA NOT NULL,
|
||||
load_amount BYTEA 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;
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
181
db/l2db/l2db_test.go
Normal file
181
db/l2db/l2db_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
37
db/l2db/migrations/001_init.sql
Normal file
37
db/l2db/migrations/001_init.sql
Normal file
@@ -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;
|
||||
40
db/utils.go
40
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
|
||||
}
|
||||
|
||||
1
go.mod
1
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
|
||||
|
||||
25
go.sum
25
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=
|
||||
|
||||
Reference in New Issue
Block a user