Browse Source

Add GET histroy-transactions endpoint

feature/sql-semaphore1
Arnau B 4 years ago
parent
commit
85fe885265
21 changed files with 4114 additions and 88 deletions
  1. +19
    -0
      api/README.md
  2. +77
    -0
      api/api.go
  3. +621
    -0
      api/api_test.go
  4. +130
    -0
      api/dbtoapistructs.go
  5. +32
    -0
      api/docker-compose.yml
  6. +204
    -0
      api/handlers.go
  7. +204
    -0
      api/parsers.go
  8. +282
    -0
      api/parsers_test.go
  9. +29
    -0
      api/run.sh
  10. +2266
    -0
      api/swagger.yml
  11. +16
    -0
      common/fee.go
  12. +5
    -5
      common/tx.go
  13. +109
    -1
      db/historydb/historydb.go
  14. +3
    -12
      db/historydb/historydb_test.go
  15. +5
    -4
      db/historydb/migrations/001_init.sql
  16. +46
    -0
      db/historydb/views.go
  17. +5
    -2
      db/l2db/l2db.go
  18. +14
    -20
      db/l2db/l2db_test.go
  19. +4
    -3
      go.mod
  20. +38
    -38
      go.sum
  21. +5
    -3
      test/historydb.go

+ 19
- 0
api/README.md

@ -0,0 +1,19 @@
# Hermez API
Easy to deploy and scale API for Hermez operators.
You will need to have [docker](https://docs.docker.com/engine/install/) and [docker-compose](https://docs.docker.com/compose/install/) installed on your machine in order to use this repo.
## Documentation
As of now the documentation is not hosted anywhere, but you can easily do it yourself by running `./run.sh doc` and then [opening the documentation in your browser](http://localhost:8001)
## Mock Up
To use a mock up of the endpoints in the API run `./run.sh doc` (UI + mock up server) or `./run.sh mock` (only mock up server). You can play with the mocked up endpoints using the [web UI](http://localhost:8001), importing `swagger.yml` into Postman or using your preferred language and using `http://loclahost:4010` as base URL.
## Editor
It is recomended to edit `swagger.yml` using a dedicated editor as they provide spec validation and real time visualization. Of course you can use your prefered editor. To use the editor run `./run.sh editor` and then [opening the editor in your browser](http://localhost:8002).
**Keep in mind that you will need to manually save the file otherwise you will lose the changes** you made once you close your browser seshion or stop the server.
**Note:** Your browser may cache the swagger definition, so in order to see updated changes it may be needed to refresh the page without cache (Ctrl + Shift + R).

+ 77
- 0
api/api.go

@ -0,0 +1,77 @@
package api
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/hermeznetwork/hermez-node/db/historydb"
"github.com/hermeznetwork/hermez-node/db/l2db"
"github.com/hermeznetwork/hermez-node/db/statedb"
)
var h *historydb.HistoryDB
var s *statedb.StateDB // Not 100% sure if this is needed
var l2 *l2db.L2DB
// SetAPIEndpoints sets the endpoints and the appropriate handlers, but doesn't start the server
func SetAPIEndpoints(
coordinatorEndpoints, explorerEndpoints bool,
server *gin.Engine,
hdb *historydb.HistoryDB,
sdb *statedb.StateDB,
l2db *l2db.L2DB,
) error {
// Check input
// TODO: is stateDB only needed for explorer endpoints or for both?
if coordinatorEndpoints && l2db == nil {
return errors.New("cannot serve Coordinator endpoints without L2DB")
}
if explorerEndpoints && hdb == nil {
return errors.New("cannot serve Explorer endpoints without HistoryDB")
}
h = hdb
s = sdb
l2 = l2db
// tmp
fmt.Println(h, s, l2)
// Add coordinator endpoints
if coordinatorEndpoints {
// Account
server.POST("/account-creation-authorization", postAccountCreationAuth)
server.GET("/account-creation-authorization/:hermezEthereumAddress", getAccountCreationAuth)
// Transaction
server.POST("/transactions-pool", postPoolTx)
server.POST("/transactions-pool/:id", getPoolTx)
}
// Add explorer endpoints
if explorerEndpoints {
// Account
server.GET("/accounts", getAccounts)
server.GET("/accounts/:hermezEthereumAddress/:accountIndex", getAccount)
server.GET("/exits", getExits)
server.GET("/exits/:batchNum/:accountIndex", getExit)
// Transaction
server.GET("/transactions-history", getHistoryTxs)
server.GET("/transactions-history/:id", getHistoryTx)
// Status
server.GET("/batches", getBatches)
server.GET("/batches/:batchNum", getBatch)
server.GET("/full-batches/:batchNum", getFullBatch)
server.GET("/slots", getSlots)
server.GET("/bids", getBids)
server.GET("/next-forgers", getNextForgers)
server.GET("/state", getState)
server.GET("/config", getConfig)
server.GET("/tokens", getTokens)
server.GET("/tokens/:id", getToken)
server.GET("/recommendedFee", getRecommendedFee)
server.GET("/coordinators", getCoordinators)
server.GET("/coordinators/:forgerAddr", getCoordinator)
}
return nil
}

+ 621
- 0
api/api_test.go

@ -0,0 +1,621 @@
package api
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"os"
"sort"
"strconv"
"testing"
"time"
ethCommon "github.com/ethereum/go-ethereum/common"
swagger "github.com/getkin/kin-openapi/openapi3filter"
"github.com/gin-gonic/gin"
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/db/historydb"
"github.com/hermeznetwork/hermez-node/db/l2db"
"github.com/hermeznetwork/hermez-node/db/statedb"
"github.com/hermeznetwork/hermez-node/log"
"github.com/hermeznetwork/hermez-node/test"
"github.com/iden3/go-iden3-crypto/babyjub"
"github.com/jinzhu/copier"
"github.com/stretchr/testify/assert"
)
const apiPort = ":4010"
const apiURL = "http://localhost" + apiPort + "/"
type testCommon struct {
blocks []common.Block
tokens []common.Token
batches []common.Batch
usrAddr string
usrBjj string
accs []common.Account
usrTxs historyTxAPIs
othrTxs historyTxAPIs
allTxs historyTxAPIs
router *swagger.Router
}
type historyTxAPIs []historyTxAPI
func (h historyTxAPIs) Len() int { return len(h) }
func (h historyTxAPIs) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h historyTxAPIs) Less(i, j int) bool {
// i not forged yet
if h[i].BatchNum == nil {
if h[j].BatchNum != nil { // j is already forged
return false
}
// Both aren't forged, is i in a smaller position?
return h[i].Position < h[j].Position
}
// i is forged
if h[j].BatchNum == nil {
return true // j is not forged
}
// Both are forged
if *h[i].BatchNum == *h[j].BatchNum {
// At the same batch, is i in a smaller position?
return h[i].Position < h[j].Position
}
// At different batches, is i in a smaller batch?
return *h[i].BatchNum < *h[j].BatchNum
}
var tc testCommon
func TestMain(m *testing.M) {
// Init swagger
router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
// Init DBs
pass := os.Getenv("POSTGRES_PASS")
hdb, err := historydb.NewHistoryDB(5432, "localhost", "hermez", pass, "history")
if err != nil {
panic(err)
}
dir, err := ioutil.TempDir("", "tmpdb")
if err != nil {
panic(err)
}
sdb, err := statedb.NewStateDB(dir, false, 0)
if err != nil {
panic(err)
}
l2db, err := l2db.NewL2DB(5432, "localhost", "hermez", pass, "l2", 10, 512, 24*time.Hour)
if err != nil {
panic(err)
}
// Init API
api := gin.Default()
if err := SetAPIEndpoints(
true,
true,
api,
hdb,
sdb,
l2db,
); err != nil {
panic(err)
}
// Start server
server := &http.Server{Addr: apiPort, Handler: api}
go func() {
if err := server.ListenAndServe(); err != nil &&
err != http.ErrServerClosed {
panic(err)
}
}()
// Populate DBs
// Clean DB
err = h.Reorg(0)
if err != nil {
panic(err)
}
// Gen blocks and add them to DB
const nBlocks = 5
blocks := test.GenBlocks(1, nBlocks+1)
err = h.AddBlocks(blocks)
if err != nil {
panic(err)
}
// Gen tokens and add them to DB
const nTokens = 10
tokens := test.GenTokens(nTokens, blocks)
err = h.AddTokens(tokens)
if err != nil {
panic(err)
}
// Gen batches and add them to DB
const nBatches = 10
batches := test.GenBatches(nBatches, blocks)
err = h.AddBatches(batches)
if err != nil {
panic(err)
}
// Gen accounts and add them to DB
const totalAccounts = 40
const userAccounts = 4
usrAddr := ethCommon.BigToAddress(big.NewInt(4896847))
privK := babyjub.NewRandPrivKey()
usrBjj := privK.Public()
accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches)
err = h.AddAccounts(accs)
if err != nil {
panic(err)
}
// Gen L1Txs and add them to DB
const totalL1Txs = 40
const userL1Txs = 4
usrL1Txs, othrL1Txs := test.GenL1Txs(0, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches)
var l1Txs []common.L1Tx
l1Txs = append(l1Txs, usrL1Txs...)
l1Txs = append(l1Txs, othrL1Txs...)
err = h.AddL1Txs(l1Txs)
if err != nil {
panic(err)
}
// Gen L2Txs and add them to DB
const totalL2Txs = 20
const userL2Txs = 4
usrL2Txs, othrL2Txs := test.GenL2Txs(totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches)
var l2Txs []common.L2Tx
l2Txs = append(l2Txs, usrL2Txs...)
l2Txs = append(l2Txs, othrL2Txs...)
err = h.AddL2Txs(l2Txs)
if err != nil {
panic(err)
}
// Set test commons
txsToAPITxs := func(l1Txs []common.L1Tx, l2Txs []common.L2Tx, blocks []common.Block, tokens []common.Token) historyTxAPIs {
// Transform L1Txs and L2Txs to generic Txs
genericTxs := []*common.Tx{}
for _, l1tx := range l1Txs {
genericTxs = append(genericTxs, l1tx.Tx())
}
for _, l2tx := range l2Txs {
genericTxs = append(genericTxs, l2tx.Tx())
}
// Transform generic Txs to HistoryTx
historyTxs := []*historydb.HistoryTx{}
for _, genericTx := range genericTxs {
// find timestamp
var timestamp time.Time
for i := 0; i < len(blocks); i++ {
if blocks[i].EthBlockNum == genericTx.EthBlockNum {
timestamp = blocks[i].Timestamp
break
}
}
// find token
token := common.Token{}
for i := 0; i < len(tokens); i++ {
if tokens[i].TokenID == genericTx.TokenID {
token = tokens[i]
break
}
}
historyTxs = append(historyTxs, &historydb.HistoryTx{
IsL1: genericTx.IsL1,
TxID: genericTx.TxID,
Type: genericTx.Type,
Position: genericTx.Position,
FromIdx: genericTx.FromIdx,
ToIdx: genericTx.ToIdx,
Amount: genericTx.Amount,
AmountFloat: genericTx.AmountFloat,
TokenID: genericTx.TokenID,
USD: token.USD * genericTx.AmountFloat,
BatchNum: genericTx.BatchNum,
EthBlockNum: genericTx.EthBlockNum,
ToForgeL1TxsNum: genericTx.ToForgeL1TxsNum,
UserOrigin: genericTx.UserOrigin,
FromEthAddr: genericTx.FromEthAddr,
FromBJJ: genericTx.FromBJJ,
LoadAmount: genericTx.LoadAmount,
LoadAmountFloat: genericTx.LoadAmountFloat,
LoadAmountUSD: token.USD * genericTx.LoadAmountFloat,
Fee: genericTx.Fee,
FeeUSD: genericTx.Fee.Percentage() * token.USD * genericTx.AmountFloat,
Nonce: genericTx.Nonce,
Timestamp: timestamp,
TokenSymbol: token.Symbol,
CurrentUSD: token.USD * genericTx.AmountFloat,
USDUpdate: token.USDUpdate,
})
}
return historyTxAPIs(historyTxsToAPI(historyTxs))
}
usrTxs := txsToAPITxs(usrL1Txs, usrL2Txs, blocks, tokens)
sort.Sort(usrTxs)
othrTxs := txsToAPITxs(othrL1Txs, othrL2Txs, blocks, tokens)
sort.Sort(othrTxs)
allTxs := append(usrTxs, othrTxs...)
sort.Sort(allTxs)
tc = testCommon{
blocks: blocks,
tokens: tokens,
batches: batches,
usrAddr: "hez:" + usrAddr.String(),
usrBjj: bjjToString(usrBjj),
accs: accs,
usrTxs: usrTxs,
othrTxs: othrTxs,
allTxs: allTxs,
router: router,
}
// Run tests
result := m.Run()
// Stop server
if err := server.Shutdown(context.Background()); err != nil {
panic(err)
}
if err := h.Close(); err != nil {
panic(err)
}
if err := l2.Close(); err != nil {
panic(err)
}
os.Exit(result)
}
func TestGetHistoryTxs(t *testing.T) {
endpoint := apiURL + "transactions-history"
fetchedTxs := historyTxAPIs{}
appendIter := func(intr interface{}) {
for i := 0; i < len(intr.(*historyTxsAPI).Txs); i++ {
tmp := &historyTxAPI{}
if err := copier.Copy(tmp, &intr.(*historyTxsAPI).Txs[i]); err != nil {
panic(err)
}
fetchedTxs = append(fetchedTxs, *tmp)
}
}
// Get all (no filters)
limit := 8
path := fmt.Sprintf("%s?limit=%d&offset=", endpoint, limit)
err := doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
// Get by ethAddr
fetchedTxs = historyTxAPIs{}
limit = 7
path = fmt.Sprintf(
"%s?hermezEthereumAddress=%s&limit=%d&offset=",
endpoint, tc.usrAddr, limit,
)
err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
// Get by bjj
fetchedTxs = historyTxAPIs{}
limit = 6
path = fmt.Sprintf(
"%s?BJJ=%s&limit=%d&offset=",
endpoint, tc.usrBjj, limit,
)
err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
// Get by tokenID
fetchedTxs = historyTxAPIs{}
limit = 5
tokenID := tc.allTxs[0].TokenID
path = fmt.Sprintf(
"%s?tokenId=%d&limit=%d&offset=",
endpoint, tokenID, limit,
)
err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
tokenIDTxs := historyTxAPIs{}
for i := 0; i < len(tc.allTxs); i++ {
if tc.allTxs[i].TokenID == tokenID {
tokenIDTxs = append(tokenIDTxs, tc.allTxs[i])
}
}
assertHistoryTxAPIs(t, tokenIDTxs, fetchedTxs)
// idx
fetchedTxs = historyTxAPIs{}
limit = 4
idx := tc.allTxs[0].FromIdx
path = fmt.Sprintf(
"%s?accountIndex=%s&limit=%d&offset=",
endpoint, idx, limit,
)
err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
idxTxs := historyTxAPIs{}
for i := 0; i < len(tc.allTxs); i++ {
if tc.allTxs[i].FromIdx == idx {
idxTxs = append(idxTxs, tc.allTxs[i])
}
}
assertHistoryTxAPIs(t, idxTxs, fetchedTxs)
// batchNum
fetchedTxs = historyTxAPIs{}
limit = 3
batchNum := tc.allTxs[0].BatchNum
path = fmt.Sprintf(
"%s?batchNum=%d&limit=%d&offset=",
endpoint, *batchNum, limit,
)
err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
batchNumTxs := historyTxAPIs{}
for i := 0; i < len(tc.allTxs); i++ {
if tc.allTxs[i].BatchNum != nil &&
*tc.allTxs[i].BatchNum == *batchNum {
batchNumTxs = append(batchNumTxs, tc.allTxs[i])
}
}
assertHistoryTxAPIs(t, batchNumTxs, fetchedTxs)
// type
txTypes := []common.TxType{
common.TxTypeExit,
common.TxTypeWithdrawn,
common.TxTypeTransfer,
common.TxTypeDeposit,
common.TxTypeCreateAccountDeposit,
common.TxTypeCreateAccountDepositTransfer,
common.TxTypeDepositTransfer,
common.TxTypeForceTransfer,
common.TxTypeForceExit,
common.TxTypeTransferToEthAddr,
common.TxTypeTransferToBJJ,
}
for _, txType := range txTypes {
fetchedTxs = historyTxAPIs{}
limit = 2
path = fmt.Sprintf(
"%s?type=%s&limit=%d&offset=",
endpoint, txType, limit,
)
err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
txTypeTxs := historyTxAPIs{}
for i := 0; i < len(tc.allTxs); i++ {
if tc.allTxs[i].Type == txType {
txTypeTxs = append(txTypeTxs, tc.allTxs[i])
}
}
assertHistoryTxAPIs(t, txTypeTxs, fetchedTxs)
}
// Multiple filters
fetchedTxs = historyTxAPIs{}
limit = 1
path = fmt.Sprintf(
"%s?batchNum=%d&tokeId=%d&limit=%d&offset=",
endpoint, *batchNum, tokenID, limit,
)
err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
assert.NoError(t, err)
mixedTxs := historyTxAPIs{}
for i := 0; i < len(tc.allTxs); i++ {
if tc.allTxs[i].BatchNum != nil {
if *tc.allTxs[i].BatchNum == *batchNum && tc.allTxs[i].TokenID == tokenID {
mixedTxs = append(mixedTxs, tc.allTxs[i])
}
}
}
assertHistoryTxAPIs(t, mixedTxs, fetchedTxs)
// All, in reverse order
fetchedTxs = historyTxAPIs{}
limit = 5
path = fmt.Sprintf("%s?", endpoint)
appendIterRev := func(intr interface{}) {
tmpAll := historyTxAPIs{}
for i := 0; i < len(intr.(*historyTxsAPI).Txs); i++ {
tmpItem := &historyTxAPI{}
if err := copier.Copy(tmpItem, &intr.(*historyTxsAPI).Txs[i]); err != nil {
panic(err)
}
tmpAll = append(tmpAll, *tmpItem)
}
fetchedTxs = append(tmpAll, fetchedTxs...)
}
err = doGoodReqPaginatedReverse(path, &historyTxsAPI{}, appendIterRev, limit)
assert.NoError(t, err)
assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
// 400
path = fmt.Sprintf(
"%s?accountIndex=%s&hermezEthereumAddress=%s",
endpoint, idx, tc.usrAddr,
)
err = doBadReq("GET", path, nil, 400)
assert.NoError(t, err)
path = fmt.Sprintf("%s?tokenId=X", endpoint)
err = doBadReq("GET", path, nil, 400)
assert.NoError(t, err)
// 404
path = fmt.Sprintf("%s?batchNum=999999", endpoint)
err = doBadReq("GET", path, nil, 404)
assert.NoError(t, err)
path = fmt.Sprintf("%s?limit=1000&offset=1000", endpoint)
err = doBadReq("GET", path, nil, 404)
assert.NoError(t, err)
}
func assertHistoryTxAPIs(t *testing.T, expected, actual historyTxAPIs) {
assert.Equal(t, len(expected), len(actual))
for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
assert.Equal(t, expected[i].Timestamp.Unix(), actual[i].Timestamp.Unix())
expected[i].Timestamp = actual[i].Timestamp
assert.Equal(t, expected[i].USDUpdate.Unix(), actual[i].USDUpdate.Unix())
expected[i].USDUpdate = actual[i].USDUpdate
if expected[i].L2Info != nil {
if expected[i].L2Info.FeeUSD > actual[i].L2Info.FeeUSD {
assert.Less(t, 0.999, actual[i].L2Info.FeeUSD/expected[i].L2Info.FeeUSD)
} else if expected[i].L2Info.FeeUSD < actual[i].L2Info.FeeUSD {
assert.Less(t, 0.999, expected[i].L2Info.FeeUSD/actual[i].L2Info.FeeUSD)
}
expected[i].L2Info.FeeUSD = actual[i].L2Info.FeeUSD
}
assert.Equal(t, expected[i], actual[i])
}
}
func doGoodReqPaginated(
path string,
iterStruct paginationer,
appendIter func(res interface{}),
) error {
next := 0
for {
// Call API to get this iteration items
if err := doGoodReq("GET", path+strconv.Itoa(next), nil, iterStruct); err != nil {
return err
}
appendIter(iterStruct)
// Keep iterating?
pag := iterStruct.GetPagination()
if pag.LastReturnedItem == pag.TotalItems-1 { // No
break
} else { // Yes
next = int(pag.LastReturnedItem + 1)
}
}
return nil
}
func doGoodReqPaginatedReverse(
path string,
iterStruct paginationer,
appendIter func(res interface{}),
limit int,
) error {
next := 0
first := true
for {
// Call API to get this iteration items
if first {
first = false
pagQuery := fmt.Sprintf("last=true&limit=%d", limit)
if err := doGoodReq("GET", path+pagQuery, nil, iterStruct); err != nil {
return err
}
} else {
pagQuery := fmt.Sprintf("offset=%d&limit=%d", next, limit)
if err := doGoodReq("GET", path+pagQuery, nil, iterStruct); err != nil {
return err
}
}
appendIter(iterStruct)
// Keep iterating?
pag := iterStruct.GetPagination()
if iterStruct.Len() == pag.TotalItems || pag.LastReturnedItem-iterStruct.Len() == -1 { // No
break
} else { // Yes
prevOffset := next
next = pag.LastReturnedItem - iterStruct.Len() - limit + 1
if next < 0 {
next = 0
limit = prevOffset
}
}
}
return nil
}
func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
ctx := context.Background()
client := &http.Client{}
httpReq, _ := http.NewRequest(method, path, reqBody)
route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
if err != nil {
return err
}
// Validate request against swagger spec
requestValidationInput := &swagger.RequestValidationInput{
Request: httpReq,
PathParams: pathParams,
Route: route,
}
if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
return err
}
// Do API call
resp, err := client.Do(httpReq)
if err != nil {
return err
}
if resp.Body == nil {
return errors.New("Nil body")
}
//nolint
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf("%d response: %s", resp.StatusCode, string(body))
}
// Unmarshal body into return struct
if err := json.Unmarshal(body, returnStruct); err != nil {
return err
}
// Validate response against swagger spec
responseValidationInput := &swagger.ResponseValidationInput{
RequestValidationInput: requestValidationInput,
Status: resp.StatusCode,
Header: resp.Header,
}
responseValidationInput = responseValidationInput.SetBodyBytes(body)
return swagger.ValidateResponse(ctx, responseValidationInput)
}
func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
ctx := context.Background()
client := &http.Client{}
httpReq, _ := http.NewRequest(method, path, reqBody)
route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
if err != nil {
return err
}
// Validate request against swagger spec
requestValidationInput := &swagger.RequestValidationInput{
Request: httpReq,
PathParams: pathParams,
Route: route,
}
if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
if expectedResponseCode != 400 {
return err
}
log.Warn("The request does not match the API spec")
}
// Do API call
resp, err := client.Do(httpReq)
if err != nil {
return err
}
if resp.Body == nil {
return errors.New("Nil body")
}
//nolint
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != expectedResponseCode {
return fmt.Errorf("Unexpected response code: %d", resp.StatusCode)
}
// Validate response against swagger spec
responseValidationInput := &swagger.ResponseValidationInput{
RequestValidationInput: requestValidationInput,
Status: resp.StatusCode,
Header: resp.Header,
}
responseValidationInput = responseValidationInput.SetBodyBytes(body)
return swagger.ValidateResponse(ctx, responseValidationInput)
}

+ 130
- 0
api/dbtoapistructs.go

@ -0,0 +1,130 @@
package api
import (
"encoding/base64"
"strconv"
"time"
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/db/historydb"
"github.com/iden3/go-iden3-crypto/babyjub"
)
// Commons of the API
type pagination struct {
TotalItems int `json:"totalItems"`
LastReturnedItem int `json:"lastReturnedItem"`
}
type paginationer interface {
GetPagination() pagination
Len() int
}
type errorMsg struct {
Message string
}
// History Tx related
type historyTxsAPI struct {
Txs []historyTxAPI `json:"transactions"`
Pagination pagination `json:"pagination"`
}
func (htx *historyTxsAPI) GetPagination() pagination { return htx.Pagination }
func (htx *historyTxsAPI) Len() int { return len(htx.Txs) }
type l1Info struct {
ToForgeL1TxsNum int64 `json:"toForgeL1TransactionsNum"`
UserOrigin bool `json:"userOrigin"`
FromEthAddr string `json:"fromEthereumAddress"`
FromBJJ string `json:"fromBJJ"`
LoadAmount string `json:"loadAmount"`
LoadAmountUSD float64 `json:"loadAmountUSD"`
EthBlockNum int64 `json:"ethereumBlockNum"`
}
type l2Info struct {
Fee common.FeeSelector `json:"fee"`
FeeUSD float64 `json:"feeUSD"`
Nonce common.Nonce `json:"nonce"`
}
type historyTxAPI struct {
IsL1 string `json:"L1orL2"`
TxID common.TxID `json:"id"`
Type common.TxType `json:"type"`
Position int `json:"position"`
FromIdx string `json:"fromAccountIndex"`
ToIdx string `json:"toAccountIndex"`
Amount string `json:"amount"`
BatchNum *common.BatchNum `json:"batchNum"`
TokenID common.TokenID `json:"tokenId"`
TokenSymbol string `json:"tokenSymbol"`
USD float64 `json:"historicUSD"`
Timestamp time.Time `json:"timestamp"`
CurrentUSD float64 `json:"currentUSD"`
USDUpdate time.Time `json:"fiatUpdate"`
L1Info *l1Info `json:"L1Info"`
L2Info *l2Info `json:"L2Info"`
}
func historyTxsToAPI(dbTxs []*historydb.HistoryTx) []historyTxAPI {
apiTxs := []historyTxAPI{}
for i := 0; i < len(dbTxs); i++ {
apiTx := historyTxAPI{
TxID: dbTxs[i].TxID,
Type: dbTxs[i].Type,
Position: dbTxs[i].Position,
FromIdx: "hez:" + dbTxs[i].TokenSymbol + ":" + strconv.Itoa(int(dbTxs[i].FromIdx)),
ToIdx: "hez:" + dbTxs[i].TokenSymbol + ":" + strconv.Itoa(int(dbTxs[i].ToIdx)),
Amount: dbTxs[i].Amount.String(),
TokenID: dbTxs[i].TokenID,
USD: dbTxs[i].USD,
BatchNum: nil,
Timestamp: dbTxs[i].Timestamp,
TokenSymbol: dbTxs[i].TokenSymbol,
CurrentUSD: dbTxs[i].CurrentUSD,
USDUpdate: dbTxs[i].USDUpdate,
L1Info: nil,
L2Info: nil,
}
bn := dbTxs[i].BatchNum
if dbTxs[i].BatchNum != 0 {
apiTx.BatchNum = &bn
}
if dbTxs[i].IsL1 {
apiTx.IsL1 = "L1"
apiTx.L1Info = &l1Info{
ToForgeL1TxsNum: dbTxs[i].ToForgeL1TxsNum,
UserOrigin: dbTxs[i].UserOrigin,
FromEthAddr: "hez:" + dbTxs[i].FromEthAddr.String(),
FromBJJ: bjjToString(dbTxs[i].FromBJJ),
LoadAmount: dbTxs[i].LoadAmount.String(),
LoadAmountUSD: dbTxs[i].LoadAmountUSD,
EthBlockNum: dbTxs[i].EthBlockNum,
}
} else {
apiTx.IsL1 = "L2"
apiTx.L2Info = &l2Info{
Fee: dbTxs[i].Fee,
FeeUSD: dbTxs[i].FeeUSD,
Nonce: dbTxs[i].Nonce,
}
}
apiTxs = append(apiTxs, apiTx)
}
return apiTxs
}
func bjjToString(bjj *babyjub.PublicKey) string {
pkComp := [32]byte(bjj.Compress())
sum := pkComp[0]
for i := 1; i < len(pkComp); i++ {
sum += pkComp[i]
}
bjjSum := append(pkComp[:], sum)
return "hez:" + base64.RawURLEncoding.EncodeToString(bjjSum)
}

+ 32
- 0
api/docker-compose.yml

@ -0,0 +1,32 @@
version: "3"
services:
hermez-api-doc:
container_name: hermez-api-doc
image: swaggerapi/swagger-ui
restart: unless-stopped
ports:
- 8001:8080
volumes:
- .:/spec
environment:
- SWAGGER_JSON=/spec/swagger.yml
hermez-api-mock:
container_name: hermez-api-mock
image: stoplight/prism
restart: unless-stopped
ports:
- 4010:4010
volumes:
- .:/spec
command: mock -h 0.0.0.0 "/spec/swagger.yml"
#docker run -d -p 80:8080 -e URL=/foo/swagger.json -v /bar:/usr/share/nginx/html/foo swaggerapi/swagger-editor
hermez-api-editor:
container_name: hermez-api-editor
image: swaggerapi/swagger-editor
restart: unless-stopped
ports:
- 8002:8080
volumes:
- .:/spec
environment:
- SWAGGER_FILE=/spec/swagger.yml

+ 204
- 0
api/handlers.go

@ -0,0 +1,204 @@
package api
import (
"database/sql"
"errors"
"net/http"
"github.com/gin-gonic/gin"
)
// maxLimit is the max permited items to be returned in paginated responses
const maxLimit uint = 2049
// dfltLast indicates how paginated endpoints use the query param last if not provided
const dfltLast = false
// dfltLimit indicates the limit of returned items in paginated responses if the query param limit is not provided
const dfltLimit uint = 20
// 2^32 -1
const maxUint32 = 4294967295
func postAccountCreationAuth(c *gin.Context) {
}
func getAccountCreationAuth(c *gin.Context) {
}
func postPoolTx(c *gin.Context) {
}
func getPoolTx(c *gin.Context) {
}
func getAccounts(c *gin.Context) {
}
func getAccount(c *gin.Context) {
}
func getExits(c *gin.Context) {
}
func getExit(c *gin.Context) {
}
func getHistoryTxs(c *gin.Context) {
// Get query parameters
// TokenID
tokenID, err := parseQueryUint("tokenId", nil, 0, maxUint32, c)
if err != nil {
retBadReq(err, c)
return
}
// Hez Eth addr
addr, err := parseQueryHezEthAddr(c)
if err != nil {
retBadReq(err, c)
return
}
// BJJ
bjj, err := parseQueryBJJ(c)
if err != nil {
retBadReq(err, c)
return
}
if addr != nil && bjj != nil {
retBadReq(errors.New("bjj and hermezEthereumAddress params are incompatible"), c)
return
}
// Idx
idx, err := parseIdx(c)
if err != nil {
retBadReq(err, c)
return
}
if idx != nil && (addr != nil || bjj != nil || tokenID != nil) {
retBadReq(errors.New("accountIndex is incompatible with BJJ, hermezEthereumAddress and tokenId"), c)
return
}
// BatchNum
batchNum, err := parseQueryUint("batchNum", nil, 0, maxUint32, c)
if err != nil {
retBadReq(err, c)
return
}
// TxType
txType, err := parseQueryTxType(c)
if err != nil {
retBadReq(err, c)
return
}
// Pagination
offset, last, limit, err := parsePagination(c)
if err != nil {
retBadReq(err, c)
return
}
// Fetch txs from historyDB
txs, totalItems, err := h.GetHistoryTxs(
addr, bjj, tokenID, idx, batchNum, txType, offset, limit, *last,
)
if err != nil {
retSQLErr(err, c)
return
}
// Build succesfull response
apiTxs := historyTxsToAPI(txs)
lastRet := int(*offset) + len(apiTxs) - 1
if *last {
lastRet = totalItems - 1
}
c.JSON(http.StatusOK, &historyTxsAPI{
Txs: apiTxs,
Pagination: pagination{
TotalItems: totalItems,
LastReturnedItem: lastRet,
},
})
}
func getHistoryTx(c *gin.Context) {
}
func getBatches(c *gin.Context) {
}
func getBatch(c *gin.Context) {
}
func getFullBatch(c *gin.Context) {
}
func getSlots(c *gin.Context) {
}
func getBids(c *gin.Context) {
}
func getNextForgers(c *gin.Context) {
}
func getState(c *gin.Context) {
}
func getConfig(c *gin.Context) {
}
func getTokens(c *gin.Context) {
}
func getToken(c *gin.Context) {
}
func getRecommendedFee(c *gin.Context) {
}
func getCoordinators(c *gin.Context) {
}
func getCoordinator(c *gin.Context) {
}
func retSQLErr(err error, c *gin.Context) {
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, errorMsg{
Message: err.Error(),
})
} else {
c.JSON(http.StatusInternalServerError, errorMsg{
Message: err.Error(),
})
}
}
func retBadReq(err error, c *gin.Context) {
c.JSON(http.StatusBadRequest, errorMsg{
Message: err.Error(),
})
}

+ 204
- 0
api/parsers.go

@ -0,0 +1,204 @@
package api
import (
"encoding/base64"
"errors"
"fmt"
"strconv"
"strings"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common"
"github.com/iden3/go-iden3-crypto/babyjub"
)
type querier interface {
Query(string) string
}
func parsePagination(c querier) (*uint, *bool, *uint, error) {
// Offset
offset := new(uint)
*offset = 0
offset, err := parseQueryUint("offset", offset, 0, maxUint32, c)
if err != nil {
return nil, nil, nil, err
}
// Last
last := new(bool)
*last = dfltLast
last, err = parseQueryBool("last", last, c)
if err != nil {
return nil, nil, nil, err
}
if *last && (offset != nil && *offset > 0) {
return nil, nil, nil, errors.New(
"last and offset are incompatible, provide only one of them",
)
}
// Limit
limit := new(uint)
*limit = dfltLimit
limit, err = parseQueryUint("limit", limit, 1, maxLimit, c)
if err != nil {
return nil, nil, nil, err
}
return offset, last, limit, nil
}
func parseQueryUint(name string, dflt *uint, min, max uint, c querier) (*uint, error) { //nolint:SA4009 res may be not overwriten
str := c.Query(name)
if str != "" {
resInt, err := strconv.Atoi(str)
if err != nil || resInt < 0 || resInt < int(min) || resInt > int(max) {
return nil, fmt.Errorf(
"Inavlid %s. Must be an integer within the range [%d, %d]",
name, min, max)
}
res := uint(resInt)
return &res, nil
}
return dflt, nil
}
func parseQueryBool(name string, dflt *bool, c querier) (*bool, error) { //nolint:SA4009 res may be not overwriten
str := c.Query(name)
if str == "" {
return dflt, nil
}
if str == "true" {
res := new(bool)
*res = true
return res, nil
}
if str == "false" {
res := new(bool)
*res = false
return res, nil
}
return nil, fmt.Errorf("Inavlid %s. Must be eithe true or false", name)
}
func parseQueryHezEthAddr(c querier) (*ethCommon.Address, error) {
const name = "hermezEthereumAddress"
addrStr := c.Query(name)
if addrStr == "" {
return nil, nil
}
splitted := strings.Split(addrStr, "hez:")
if len(splitted) != 2 || len(splitted[1]) != 42 {
return nil, fmt.Errorf(
"Invalid %s, must follow this regex: ^hez:0x[a-fA-F0-9]{40}$", name)
}
var addr ethCommon.Address
err := addr.UnmarshalText([]byte(splitted[1]))
return &addr, err
}
func parseQueryBJJ(c querier) (*babyjub.PublicKey, error) {
const name = "BJJ"
const decodedLen = 33
bjjStr := c.Query(name)
if bjjStr == "" {
return nil, nil
}
splitted := strings.Split(bjjStr, "hez:")
if len(splitted) != 2 || len(splitted[1]) != 44 {
return nil, fmt.Errorf(
"Invalid %s, must follow this regex: ^hez:[A-Za-z0-9+/=]{44}$",
name)
}
decoded, err := base64.RawURLEncoding.DecodeString(splitted[1])
if err != nil {
return nil, fmt.Errorf(
"Invalid %s, error decoding base64 string: %s",
name, err.Error())
}
if len(decoded) != decodedLen {
return nil, fmt.Errorf(
"invalid %s, error decoding base64 string: unexpected byte array length",
name)
}
bjjBytes := [decodedLen - 1]byte{}
copy(bjjBytes[:decodedLen-1], decoded[:decodedLen-1])
sum := bjjBytes[0]
for i := 1; i < len(bjjBytes); i++ {
sum += bjjBytes[i]
}
if decoded[decodedLen-1] != sum {
return nil, fmt.Errorf("invalid %s, checksum failed",
name)
}
bjjComp := babyjub.PublicKeyComp(bjjBytes)
bjj, err := bjjComp.Decompress()
if err != nil {
return nil, fmt.Errorf(
"invalid %s, error decompressing public key: %s",
name, err.Error())
}
return bjj, nil
}
func parseQueryTxType(c querier) (*common.TxType, error) {
const name = "type"
typeStr := c.Query(name)
if typeStr == "" {
return nil, nil
}
switch common.TxType(typeStr) {
case common.TxTypeExit:
ret := common.TxTypeExit
return &ret, nil
case common.TxTypeWithdrawn:
ret := common.TxTypeWithdrawn
return &ret, nil
case common.TxTypeTransfer:
ret := common.TxTypeTransfer
return &ret, nil
case common.TxTypeDeposit:
ret := common.TxTypeDeposit
return &ret, nil
case common.TxTypeCreateAccountDeposit:
ret := common.TxTypeCreateAccountDeposit
return &ret, nil
case common.TxTypeCreateAccountDepositTransfer:
ret := common.TxTypeCreateAccountDepositTransfer
return &ret, nil
case common.TxTypeDepositTransfer:
ret := common.TxTypeDepositTransfer
return &ret, nil
case common.TxTypeForceTransfer:
ret := common.TxTypeForceTransfer
return &ret, nil
case common.TxTypeForceExit:
ret := common.TxTypeForceExit
return &ret, nil
case common.TxTypeTransferToEthAddr:
ret := common.TxTypeTransferToEthAddr
return &ret, nil
case common.TxTypeTransferToBJJ:
ret := common.TxTypeTransferToBJJ
return &ret, nil
}
return nil, fmt.Errorf(
"invalid %s, %s is not a valid option. Check the valid options in the docmentation",
name, typeStr,
)
}
func parseIdx(c querier) (*uint, error) {
const name = "accountIndex"
addrStr := c.Query(name)
if addrStr == "" {
return nil, nil
}
splitted := strings.Split(addrStr, ":")
const expectedLen = 3
if len(splitted) != expectedLen {
return nil, fmt.Errorf(
"invalid %s, must follow this: hez:<tokenSymbol>:index", name)
}
idxInt, err := strconv.Atoi(splitted[2])
idx := uint(idxInt)
return &idx, err
}

+ 282
- 0
api/parsers_test.go

@ -0,0 +1,282 @@
package api
import (
"encoding/base64"
"math/big"
"strconv"
"testing"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common"
"github.com/iden3/go-iden3-crypto/babyjub"
"github.com/stretchr/testify/assert"
)
type queryParser struct {
m map[string]string
}
func (qp *queryParser) Query(query string) string {
if val, ok := qp.m[query]; ok {
return val
}
return ""
}
func TestParseQueryUint(t *testing.T) {
name := "foo"
c := &queryParser{}
c.m = make(map[string]string)
var min uint = 1
var max uint = 10
var dflt *uint
// Not uint
c.m[name] = "-1"
_, err := parseQueryUint(name, dflt, min, max, c)
assert.Error(t, err)
c.m[name] = "a"
_, err = parseQueryUint(name, dflt, min, max, c)
assert.Error(t, err)
c.m[name] = "0.1"
_, err = parseQueryUint(name, dflt, min, max, c)
assert.Error(t, err)
c.m[name] = "1.0"
_, err = parseQueryUint(name, dflt, min, max, c)
assert.Error(t, err)
// Out of range
c.m[name] = strconv.Itoa(int(min) - 1)
_, err = parseQueryUint(name, dflt, min, max, c)
assert.Error(t, err)
c.m[name] = strconv.Itoa(int(max) + 1)
_, err = parseQueryUint(name, dflt, min, max, c)
assert.Error(t, err)
// Default nil
c.m[name] = ""
res, err := parseQueryUint(name, dflt, min, max, c)
assert.NoError(t, err)
assert.Nil(t, res)
// Default not nil
dflt = new(uint)
*dflt = uint(min)
res, err = parseQueryUint(name, dflt, min, max, c)
assert.NoError(t, err)
assert.Equal(t, uint(min), *res)
// Correct
c.m[name] = strconv.Itoa(int(max))
res, err = parseQueryUint(name, res, min, max, c)
assert.NoError(t, err)
assert.Equal(t, uint(max), *res)
}
func TestParseQueryBool(t *testing.T) {
name := "foo"
c := &queryParser{}
c.m = make(map[string]string)
var dflt *bool
// Not bool
c.m[name] = "x"
_, err := parseQueryBool(name, dflt, c)
assert.Error(t, err)
c.m[name] = "False"
_, err = parseQueryBool(name, dflt, c)
assert.Error(t, err)
c.m[name] = "0"
_, err = parseQueryBool(name, dflt, c)
assert.Error(t, err)
c.m[name] = "1"
_, err = parseQueryBool(name, dflt, c)
assert.Error(t, err)
// Default nil
c.m[name] = ""
res, err := parseQueryBool(name, dflt, c)
assert.NoError(t, err)
assert.Nil(t, res)
// Default not nil
dflt = new(bool)
*dflt = true
res, err = parseQueryBool(name, dflt, c)
assert.NoError(t, err)
assert.True(t, *res)
// Correct
c.m[name] = "false"
res, err = parseQueryBool(name, dflt, c)
assert.NoError(t, err)
assert.False(t, *res)
c.m[name] = "true"
res, err = parseQueryBool(name, dflt, c)
assert.NoError(t, err)
assert.True(t, *res)
}
func TestParsePagination(t *testing.T) {
c := &queryParser{}
c.m = make(map[string]string)
// Offset out of range
c.m["offset"] = "-1"
_, _, _, err := parsePagination(c)
assert.Error(t, err)
c.m["offset"] = strconv.Itoa(maxUint32 + 1)
_, _, _, err = parsePagination(c)
assert.Error(t, err)
c.m["offset"] = ""
// Limit out of range
c.m["limit"] = "0"
_, _, _, err = parsePagination(c)
assert.Error(t, err)
c.m["limit"] = strconv.Itoa(int(maxLimit) + 1)
_, _, _, err = parsePagination(c)
assert.Error(t, err)
c.m["limit"] = ""
// Last and offset
c.m["offset"] = "1"
c.m["last"] = "true"
_, _, _, err = parsePagination(c)
assert.Error(t, err)
// Default
c.m["offset"] = ""
c.m["last"] = ""
c.m["limit"] = ""
offset, last, limit, err := parsePagination(c)
assert.NoError(t, err)
assert.Equal(t, 0, int(*offset))
assert.Equal(t, dfltLast, *last)
assert.Equal(t, dfltLimit, *limit)
// Correct
c.m["offset"] = ""
c.m["last"] = "true"
c.m["limit"] = "25"
offset, last, limit, err = parsePagination(c)
assert.NoError(t, err)
assert.Equal(t, 0, int(*offset))
assert.True(t, *last)
assert.Equal(t, 25, int(*limit))
c.m["offset"] = "25"
c.m["last"] = "false"
c.m["limit"] = "50"
offset, last, limit, err = parsePagination(c)
assert.NoError(t, err)
assert.Equal(t, 25, int(*offset))
assert.False(t, *last)
assert.Equal(t, 50, int(*limit))
}
func TestParseQueryHezEthAddr(t *testing.T) {
name := "hermezEthereumAddress"
c := &queryParser{}
c.m = make(map[string]string)
ethAddr := ethCommon.BigToAddress(big.NewInt(int64(347683)))
// Not HEZ Eth addr
c.m[name] = "hez:0xf"
_, err := parseQueryHezEthAddr(c)
assert.Error(t, err)
c.m[name] = ethAddr.String()
_, err = parseQueryHezEthAddr(c)
assert.Error(t, err)
c.m[name] = "hez:0xXX942cfcd25ad4d90a62358b0dd84f33b39826XX"
_, err = parseQueryHezEthAddr(c)
assert.Error(t, err)
// Default
c.m[name] = ""
res, err := parseQueryHezEthAddr(c)
assert.NoError(t, err)
assert.Nil(t, res)
// Correct
c.m[name] = "hez:" + ethAddr.String()
res, err = parseQueryHezEthAddr(c)
assert.NoError(t, err)
assert.Equal(t, ethAddr, *res)
}
func TestParseQueryBJJ(t *testing.T) {
name := "BJJ"
c := &queryParser{}
c.m = make(map[string]string)
privK := babyjub.NewRandPrivKey()
pubK := privK.Public()
pkComp := [32]byte(pubK.Compress())
// Not HEZ Eth addr
c.m[name] = "hez:abcd"
_, err := parseQueryBJJ(c)
assert.Error(t, err)
c.m[name] = pubK.String()
_, err = parseQueryBJJ(c)
assert.Error(t, err)
// Wrong checksum
bjjSum := append(pkComp[:], byte(1))
c.m[name] = "hez:" + base64.RawStdEncoding.EncodeToString(bjjSum)
_, err = parseQueryBJJ(c)
assert.Error(t, err)
// Default
c.m[name] = ""
res, err := parseQueryBJJ(c)
assert.NoError(t, err)
assert.Nil(t, res)
// Correct
c.m[name] = bjjToString(pubK)
res, err = parseQueryBJJ(c)
assert.NoError(t, err)
assert.Equal(t, *pubK, *res)
}
func TestParseQueryTxType(t *testing.T) {
name := "type"
c := &queryParser{}
c.m = make(map[string]string)
// Incorrect values
c.m[name] = "deposit"
_, err := parseQueryTxType(c)
assert.Error(t, err)
c.m[name] = "1"
_, err = parseQueryTxType(c)
assert.Error(t, err)
// Default
c.m[name] = ""
res, err := parseQueryTxType(c)
assert.NoError(t, err)
assert.Nil(t, res)
// Correct values
c.m[name] = string(common.TxTypeExit)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeExit, *res)
c.m[name] = string(common.TxTypeWithdrawn)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeWithdrawn, *res)
c.m[name] = string(common.TxTypeTransfer)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeTransfer, *res)
c.m[name] = string(common.TxTypeDeposit)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeDeposit, *res)
c.m[name] = string(common.TxTypeCreateAccountDeposit)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeCreateAccountDeposit, *res)
c.m[name] = string(common.TxTypeCreateAccountDepositTransfer)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeCreateAccountDepositTransfer, *res)
c.m[name] = string(common.TxTypeDepositTransfer)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeDepositTransfer, *res)
c.m[name] = string(common.TxTypeForceTransfer)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeForceTransfer, *res)
c.m[name] = string(common.TxTypeForceExit)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeForceExit, *res)
c.m[name] = string(common.TxTypeTransferToEthAddr)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeTransferToEthAddr, *res)
c.m[name] = string(common.TxTypeTransferToBJJ)
res, err = parseQueryTxType(c)
assert.NoError(t, err)
assert.Equal(t, common.TxTypeTransferToBJJ, *res)
}

+ 29
- 0
api/run.sh

@ -0,0 +1,29 @@
#!/bin/sh -e
USAGE="Available options:
doc Start documentation UI at http://loclahost:8001 and the mock up server at http://loclahost:4010
mock Start the mock up server at http://loclahost:4010
editor Start the documentation editor at http://loclahost:8002
stop Stop all runing services started using this script
help display this message"
case "$1" in
doc)
sudo docker-compose up -d hermez-api-doc hermez-api-mock && echo "\n\nStarted documentation UI at http://localhost:8001 and mockup server at http://localhost:4010"
;;
mock)
sudo docker-compose up -d hermez-api-mock && echo "\n\nStarted mockup server at http://localhost:4010"
;;
editor)
sudo docker-compose up -d hermez-api-editor hermez-api-mock && echo "\n\nStarted spec editor at http://localhost:8002 and mockup server at http://localhost:4010"
;;
stop)
sudo docker-compose rm -sf && echo "\n\nStopped all the services initialized by this script"
;;
help)
echo "$USAGE"
;;
*)
echo "Invalid option.\n\n$USAGE"
;;
esac

+ 2266
- 0
api/swagger.yml
File diff suppressed because it is too large
View File


+ 16
- 0
common/fee.go

@ -1,5 +1,7 @@
package common
import "math"
// Fee is a type that represents the percentage of tokens that will be paid in a transaction
// to incentivaise the materialization of it
type Fee float64
@ -16,6 +18,20 @@ type RecommendedFee struct {
// FeeSelector is used to select a percentage from the FeePlan.
type FeeSelector uint8
// Percentage returns the associated percentage of the FeeSelector
func (f FeeSelector) Percentage() float64 {
if f == 0 {
return 0
//nolint:gomnd
} else if f <= 32 { //nolint:gomnd
return math.Pow(10, -24+(float64(f)/2)) //nolint:gomnd
} else if f <= 223 { //nolint:gomnd
return math.Pow(10, -8+(0.041666666666667*(float64(f)-32))) //nolint:gomnd
} else {
return math.Pow(10, float64(f)-224) //nolint:gomnd
}
}
// MaxFeePlan is the maximum value of the FeePlan
const MaxFeePlan = 256

+ 5
- 5
common/tx.go

@ -27,15 +27,15 @@ const (
// TxTypeCreateAccountDepositTransfer represents L1->L2 transfer + L2->L2 transfer
TxTypeCreateAccountDepositTransfer TxType = "CreateAccountDepositTransfer"
// TxTypeDepositTransfer TBD
TxTypeDepositTransfer TxType = "TxTypeDepositTransfer"
TxTypeDepositTransfer TxType = "DepositTransfer"
// TxTypeForceTransfer TBD
TxTypeForceTransfer TxType = "TxTypeForceTransfer"
TxTypeForceTransfer TxType = "ForceTransfer"
// TxTypeForceExit TBD
TxTypeForceExit TxType = "TxTypeForceExit"
TxTypeForceExit TxType = "ForceExit"
// TxTypeTransferToEthAddr TBD
TxTypeTransferToEthAddr TxType = "TxTypeTransferToEthAddr"
TxTypeTransferToEthAddr TxType = "TransferToEthAddr"
// TxTypeTransferToBJJ TBD
TxTypeTransferToBJJ TxType = "TxTypeTransferToBJJ"
TxTypeTransferToBJJ TxType = "TransferToBJJ"
)
// Tx is a struct used by the TxSelector & BatchBuilder as a generic type generated from L1Tx & PoolL2Tx

+ 109
- 1
db/historydb/historydb.go

@ -1,11 +1,16 @@
package historydb
import (
"database/sql"
"errors"
"fmt"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/gobuffalo/packr/v2"
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/db"
"github.com/hermeznetwork/hermez-node/log"
"github.com/iden3/go-iden3-crypto/babyjub"
"github.com/jmoiron/sqlx"
//nolint:errcheck // driver for postgres DB
@ -37,9 +42,11 @@ func NewHistoryDB(port int, host, user, password, dbname string) (*HistoryDB, er
migrations := &migrate.PackrMigrationSource{
Box: packr.New("history-migrations", "./migrations"),
}
if _, err := migrate.Exec(hdb.DB, "postgres", migrations, migrate.Up); err != nil {
nMigrations, err := migrate.Exec(hdb.DB, "postgres", migrations, migrate.Up)
if err != nil {
return nil, err
}
log.Debug("HistoryDB applied ", nMigrations, " migrations for ", dbname, " database")
return &HistoryDB{hdb}, nil
}
@ -320,6 +327,107 @@ func (hdb *HistoryDB) GetTxs() ([]*common.Tx, error) {
return txs, err
}
// GetHistoryTxs returns a list of txs from the DB using the HistoryTx struct
func (hdb *HistoryDB) GetHistoryTxs(
ethAddr *ethCommon.Address, bjj *babyjub.PublicKey,
tokenID, idx, batchNum *uint, txType *common.TxType,
offset, limit *uint, last bool,
) ([]*HistoryTx, int, error) {
if ethAddr != nil && bjj != nil {
return nil, 0, errors.New("ethAddr and bjj are incompatible")
}
var query string
var args []interface{}
queryStr := `SELECT tx.*, tx.amount_f * token.usd AS current_usd,
token.symbol, token.usd_update, block.timestamp, count(*) OVER() AS total_items FROM tx
INNER JOIN token ON tx.token_id = token.token_id
INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
// Apply filters
nextIsAnd := false
// ethAddr filter
if ethAddr != nil {
queryStr = `WITH acc AS
(select idx from account where eth_addr = ?) ` + queryStr
queryStr += ", acc WHERE (tx.from_idx IN(acc.idx) OR tx.to_idx IN(acc.idx)) "
nextIsAnd = true
args = append(args, ethAddr)
} else if bjj != nil { // bjj filter
queryStr = `WITH acc AS
(select idx from account where bjj = ?) ` + queryStr
queryStr += ", acc WHERE (tx.from_idx IN(acc.idx) OR tx.to_idx IN(acc.idx)) "
nextIsAnd = true
args = append(args, bjj)
}
// tokenID filter
if tokenID != nil {
if nextIsAnd {
queryStr += "AND "
} else {
queryStr += "WHERE "
}
queryStr += "tx.token_id = ? "
args = append(args, tokenID)
nextIsAnd = true
}
// idx filter
if idx != nil {
if nextIsAnd {
queryStr += "AND "
} else {
queryStr += "WHERE "
}
queryStr += "(tx.from_idx = ? OR tx.to_idx = ?) "
args = append(args, idx, idx)
nextIsAnd = true
}
// batchNum filter
if batchNum != nil {
if nextIsAnd {
queryStr += "AND "
} else {
queryStr += "WHERE "
}
queryStr += "tx.batch_num = ? "
args = append(args, batchNum)
nextIsAnd = true
}
// txType filter
if txType != nil {
if nextIsAnd {
queryStr += "AND "
} else {
queryStr += "WHERE "
}
queryStr += "tx.type = ? "
args = append(args, txType)
// nextIsAnd = true
}
// pagination
if last {
queryStr += "ORDER BY (batch_num, position) DESC NULLS FIRST "
} else {
queryStr += "ORDER BY (batch_num, position) ASC NULLS LAST "
queryStr += fmt.Sprintf("OFFSET %d ", *offset)
}
queryStr += fmt.Sprintf("LIMIT %d ", *limit)
query = hdb.db.Rebind(queryStr)
// log.Debug(query)
txs := []*HistoryTx{}
if err := meddler.QueryAll(hdb.db, &txs, query, args...); err != nil {
return nil, 0, err
}
if len(txs) == 0 {
return nil, 0, sql.ErrNoRows
} else if last {
tmp := []*HistoryTx{}
for i := len(txs) - 1; i >= 0; i-- {
tmp = append(tmp, txs[i])
}
txs = tmp
}
return txs, txs[0].TotalItems, nil
}
// GetTx returns a tx from the DB
func (hdb *HistoryDB) GetTx(txID common.TxID) (*common.Tx, error) {
tx := new(common.Tx)

+ 3
- 12
db/historydb/historydb_test.go

@ -2,7 +2,6 @@ package historydb
import (
"fmt"
"math"
"math/big"
"os"
"testing"
@ -186,7 +185,7 @@ func TestAccounts(t *testing.T) {
assert.NoError(t, err)
// Generate fake accounts
const nAccounts = 3
accs := test.GenAccounts(nAccounts, 0, tokens, nil, batches)
accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
err = historyDB.AddAccounts(accs)
assert.NoError(t, err)
// Fetch accounts
@ -219,7 +218,7 @@ func TestTxs(t *testing.T) {
assert.NoError(t, err)
// Generate fake accounts
const nAccounts = 3
accs := test.GenAccounts(nAccounts, 0, tokens, nil, batches)
accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
err = historyDB.AddAccounts(accs)
assert.NoError(t, err)
// Generate fake L1 txs
@ -265,15 +264,7 @@ func TestTxs(t *testing.T) {
} else {
assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
}
if tx.Fee == 0 {
tx.FeeUSD = 0
} else if tx.Fee <= 32 {
tx.FeeUSD = tx.USD * math.Pow(10, -24+(float64(tx.Fee)/2))
} else if tx.Fee <= 223 {
tx.FeeUSD = tx.USD * math.Pow(10, -8+(0.041666666666667*(float64(tx.Fee)-32)))
} else {
tx.FeeUSD = tx.USD * math.Pow(10, float64(tx.Fee)-224)
}
tx.FeeUSD = tx.USD * tx.Fee.Percentage()
if fetchedTx.FeeUSD > tx.FeeUSD {
assert.Less(t, 0.999, tx.FeeUSD/fetchedTx.FeeUSD)
} else if fetchedTx.FeeUSD < tx.FeeUSD {

+ 5
- 4
db/historydb/migrations/001_init.sql

@ -5,7 +5,7 @@ CREATE TABLE block (
hash BYTEA NOT NULL
);
CREATE TABLE coordianator (
CREATE TABLE coordinator (
forger_addr BYTEA NOT NULL,
eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE,
withdraw_addr BYTEA NOT NULL,
@ -99,6 +99,8 @@ CREATE TABLE tx (
nonce BIGINT
);
CREATE INDEX tx_order ON tx (batch_num, position);
-- +migrate StatementBegin
CREATE FUNCTION set_tx()
RETURNS TRIGGER
@ -197,11 +199,10 @@ CREATE TABLE consensus_vars (
DROP TABLE consensus_vars;
DROP TABLE rollup_vars;
DROP TABLE account;
DROP TABLE l2tx;
DROP TABLE l1tx;
DROP TABLE tx;
DROP TABLE token;
DROP TABLE bid;
DROP TABLE exit_tree;
DROP TABLE batch;
DROP TABLE coordianator;
DROP TABLE coordinator;
DROP TABLE block;

+ 46
- 0
db/historydb/views.go

@ -0,0 +1,46 @@
package historydb
import (
"math/big"
"time"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/hermeznetwork/hermez-node/common"
"github.com/iden3/go-iden3-crypto/babyjub"
)
// HistoryTx is a representation of a generic Tx with additional information
// required by the API, and extracted by joining block and token tables
type HistoryTx struct {
// Generic
IsL1 bool `meddler:"is_l1"`
TxID common.TxID `meddler:"id"`
Type common.TxType `meddler:"type"`
Position int `meddler:"position"`
FromIdx common.Idx `meddler:"from_idx"`
ToIdx common.Idx `meddler:"to_idx"`
Amount *big.Int `meddler:"amount,bigint"`
AmountFloat float64 `meddler:"amount_f"`
TokenID common.TokenID `meddler:"token_id"`
USD float64 `meddler:"amount_usd,zeroisnull"`
BatchNum common.BatchNum `meddler:"batch_num,zeroisnull"` // batchNum in which this tx was forged. If the tx is L2, this must be != 0
EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum Block Number in which this L1Tx was added to the queue
// L1
ToForgeL1TxsNum int64 `meddler:"to_forge_l1_txs_num"` // toForgeL1TxsNum in which the tx was forged / will be forged
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
FromEthAddr ethCommon.Address `meddler:"from_eth_addr"`
FromBJJ *babyjub.PublicKey `meddler:"from_bjj"`
LoadAmount *big.Int `meddler:"load_amount,bigintnull"`
LoadAmountFloat float64 `meddler:"load_amount_f"`
LoadAmountUSD float64 `meddler:"load_amount_usd,zeroisnull"`
// L2
Fee common.FeeSelector `meddler:"fee,zeroisnull"`
FeeUSD float64 `meddler:"fee_usd,zeroisnull"`
Nonce common.Nonce `meddler:"nonce,zeroisnull"`
// API extras
Timestamp time.Time `meddler:"timestamp,utctime"`
TokenSymbol string `meddler:"symbol"`
CurrentUSD float64 `meddler:"current_usd"`
USDUpdate time.Time `meddler:"usd_update,utctime"`
TotalItems int `meddler:"total_items"`
}

+ 5
- 2
db/l2db/l2db.go

@ -8,6 +8,7 @@ import (
"github.com/gobuffalo/packr/v2"
"github.com/hermeznetwork/hermez-node/common"
"github.com/hermeznetwork/hermez-node/db"
"github.com/hermeznetwork/hermez-node/log"
"github.com/jmoiron/sqlx"
//nolint:errcheck // driver for postgres DB
@ -48,11 +49,13 @@ func NewL2DB(
// Run DB migrations
migrations := &migrate.PackrMigrationSource{
Box: packr.New("history-migrations", "./migrations"),
Box: packr.New("l2db-migrations", "./migrations"),
}
if _, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up); err != nil {
nMigrations, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
if err != nil {
return nil, err
}
log.Debug("L2DB applied ", nMigrations, " migrations for ", dbname, " database")
return &L2DB{
db: db,

+ 14
- 20
db/l2db/l2db_test.go

@ -21,7 +21,10 @@ func TestMain(m *testing.M) {
pass := os.Getenv("POSTGRES_PASS")
l2DB, err = NewL2DB(5432, "localhost", "hermez", pass, "l2", 10, 100, 24*time.Hour)
if err != nil {
log.Error("L2DB migration failed: " + err.Error())
panic(err)
} else {
log.Debug("L2DB migration succed")
}
// Run tests
result := m.Run()
@ -34,7 +37,7 @@ func TestMain(m *testing.M) {
func TestAddTx(t *testing.T) {
const nInserts = 20
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
for _, tx := range txs {
err := l2DB.AddTx(tx)
@ -52,7 +55,7 @@ func TestAddTx(t *testing.T) {
func BenchmarkAddTx(b *testing.B) {
const nInserts = 20
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
now := time.Now()
for _, tx := range txs {
@ -64,7 +67,7 @@ func BenchmarkAddTx(b *testing.B) {
func TestGetPending(t *testing.T) {
const nInserts = 20
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
var pendingTxs []*common.PoolL2Tx
for _, tx := range txs {
@ -90,7 +93,7 @@ func TestStartForging(t *testing.T) {
// Generate txs
const nInserts = 60
const fakeBatchNum common.BatchNum = 33
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
var startForgingTxIDs []common.TxID
randomizer := 0
@ -119,7 +122,7 @@ func TestDoneForging(t *testing.T) {
// Generate txs
const nInserts = 60
const fakeBatchNum common.BatchNum = 33
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
var doneForgingTxIDs []common.TxID
randomizer := 0
@ -148,7 +151,7 @@ func TestInvalidate(t *testing.T) {
// Generate txs
const nInserts = 60
const fakeBatchNum common.BatchNum = 33
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
var invalidTxIDs []common.TxID
randomizer := 0
@ -177,7 +180,7 @@ func TestCheckNonces(t *testing.T) {
// Generate txs
const nInserts = 60
const fakeBatchNum common.BatchNum = 33
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
var invalidTxIDs []common.TxID
// Generate accounts
@ -219,7 +222,7 @@ func TestCheckNonces(t *testing.T) {
func TestUpdateTxValue(t *testing.T) {
// Generate txs
const nInserts = 255 // Force all possible fee selector values
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
// Generate tokens
const nTokens = 2
@ -277,7 +280,7 @@ func TestReorg(t *testing.T) {
const nInserts = 20
const lastValidBatch common.BatchNum = 20
const reorgBatch common.BatchNum = lastValidBatch + 1
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(nInserts)
// Add txs to the DB
reorgedTxIDs := []common.TxID{}
@ -312,7 +315,7 @@ func TestReorg(t *testing.T) {
func TestPurge(t *testing.T) {
// Generate txs
nInserts := l2DB.maxTxs + 20
cleanDB()
test.CleanL2DB(l2DB.DB())
txs := test.GenPoolTxs(int(nInserts))
deletedIDs := []common.TxID{}
keepedIDs := []common.TxID{}
@ -360,7 +363,7 @@ func TestPurge(t *testing.T) {
}
func TestAuth(t *testing.T) {
cleanDB()
test.CleanL2DB(l2DB.DB())
const nAuths = 5
// Generate authorizations
auths := test.GenAuths(nAuths)
@ -378,12 +381,3 @@ func TestAuth(t *testing.T) {
assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix())
}
}
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)
}
}

+ 4
- 3
go.mod

@ -6,19 +6,20 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/dghubble/sling v1.3.0
github.com/ethereum/go-ethereum v1.9.17
github.com/getkin/kin-openapi v0.22.0
github.com/gin-gonic/gin v1.4.0
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.20200823174058-e04ca5764a15
github.com/iden3/go-merkletree v0.0.0-20200902123354-eeb949f8c334
github.com/jinzhu/gorm v1.9.15
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/jmoiron/sqlx v1.2.0
github.com/lib/pq v1.8.0
github.com/mitchellh/copystructure v1.0.0
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351
github.com/russross/meddler v1.0.0
github.com/stretchr/testify v1.6.1
github.com/urfave/cli v1.22.1
github.com/ugorji/go v1.1.8 // indirect
github.com/urfave/cli/v2 v2.2.0
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.16.0

+ 38
- 38
go.sum

@ -20,9 +20,9 @@ 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 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
@ -36,8 +36,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
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 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
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=
@ -76,6 +76,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894 h1:JLaf/iINcLyjwbtTsCJjc6rtlASgHeIJPrB6QmwURnA=
github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@ -119,7 +120,6 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vs
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
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/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=
@ -142,7 +142,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/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=
@ -155,23 +154,32 @@ github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/getkin/kin-openapi v0.22.0 h1:J5IFyKd/5yuB6AZAgwK0CMBKnabWcmkowtsl6bRkz4s=
github.com/getkin/kin-openapi v0.22.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
@ -274,6 +282,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/holiman/uint256 v1.1.0 h1:Iye6ze0DW9s+7EMn8y6Q4ebegDzpu28JQHEVM1Bq+Wg=
github.com/holiman/uint256 v1.1.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
@ -287,34 +296,10 @@ github.com/iden3/go-iden3-core v0.0.8 h1:PLw7iCiX7Pw1dqBkR+JaLQWqB5RKd+vgu25UBdv
github.com/iden3/go-iden3-core v0.0.8/go.mod h1:URNjIhMql6sEbWubIGrjJdw5wHCE1Pk1XghxjBOtA3s=
github.com/iden3/go-iden3-crypto v0.0.5 h1:inCSm5a+ry+nbpVTL/9+m6UcIwSv6nhUm0tnIxEbcps=
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-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-iden3-crypto v0.0.6-0.20200819064831-09d161e9f670 h1:gNBFu/WnRfNn+xywE04fgCWSHlb6wr0nIIll9i4R2fc=
github.com/iden3/go-iden3-crypto v0.0.6-0.20200819064831-09d161e9f670/go.mod h1:oBgthFLboAWi9feaBUFy7OxEcyn9vA1khHSL/WwWFyg=
github.com/iden3/go-iden3-crypto v0.0.6-0.20200823174058-e04ca5764a15 h1:hzCf9JY0vKUB9wVq+oXzqcei20xDtBu8XEIQIjgOowE=
github.com/iden3/go-iden3-crypto v0.0.6-0.20200823174058-e04ca5764a15/go.mod h1:oBgthFLboAWi9feaBUFy7OxEcyn9vA1khHSL/WwWFyg=
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-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=
github.com/iden3/go-merkletree v0.0.0-20200815105542-2277604e65dd/go.mod h1:/MsQOzDnxK8X/u7XP9ZBoZwZ4gIm9FlwfqckH5dRuTM=
github.com/iden3/go-merkletree v0.0.0-20200815144208-1f1bd54b93ae h1:CC8oKPM+38/Dkq20QymuIjyRo0UFzJZeH5DPbGWURrI=
github.com/iden3/go-merkletree v0.0.0-20200815144208-1f1bd54b93ae/go.mod h1:/MsQOzDnxK8X/u7XP9ZBoZwZ4gIm9FlwfqckH5dRuTM=
github.com/iden3/go-merkletree v0.0.0-20200819075941-77df3096f7ea h1:0MtMnN42ULNxOnJYa0FH1qjeSEBPkYVH6ZCs22rz2FU=
github.com/iden3/go-merkletree v0.0.0-20200819075941-77df3096f7ea/go.mod h1:MRe6i0mi2oDVUzgBIHsNRE6XAg8EBuqIQZMsd+do+dU=
github.com/iden3/go-merkletree v0.0.0-20200819092443-dc656fdd32fc h1:VnRP7JCp5TJHnTC+r8NrXGkRBvp62tRGqeA3FcdEq+Q=
github.com/iden3/go-merkletree v0.0.0-20200819092443-dc656fdd32fc/go.mod h1:MRe6i0mi2oDVUzgBIHsNRE6XAg8EBuqIQZMsd+do+dU=
github.com/iden3/go-merkletree v0.0.0-20200825093552-a4b68208bb41 h1:mCOMMQ/YmL9ST9kk7ifT961chESkB2GFFEp8osF0Jw8=
github.com/iden3/go-merkletree v0.0.0-20200825093552-a4b68208bb41/go.mod h1:MRe6i0mi2oDVUzgBIHsNRE6XAg8EBuqIQZMsd+do+dU=
github.com/iden3/go-merkletree v0.0.0-20200902123354-eeb949f8c334 h1:FQngDJKiwM6i4kHlVFvSpJa9sO+QvZ7C+GqoPWe+5BI=
github.com/iden3/go-merkletree v0.0.0-20200902123354-eeb949f8c334/go.mod h1:MRe6i0mi2oDVUzgBIHsNRE6XAg8EBuqIQZMsd+do+dU=
github.com/iden3/go-wasm3 v0.0.1/go.mod h1:j+TcAB94Dfrjlu5kJt83h2OqAU+oyNUTwNZnQyII1sI=
@ -328,10 +313,8 @@ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
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 h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o=
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s=
github.com/jinzhu/gorm v1.9.15/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
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=
@ -340,6 +323,7 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@ -367,7 +351,6 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
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=
@ -400,7 +383,6 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
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=
@ -419,8 +401,10 @@ github.com/mitchellh/mapstructure v1.3.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -442,7 +426,9 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1
github.com/olekukonko/tablewriter v0.0.2 h1:sq53g+DWf0J6/ceFUHpQ0nAEb6WgM++fq16MZ91cS6o=
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
@ -577,12 +563,17 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8=
github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.8 h1:/D9x7IRpfMHDlizVOgxrag5Fh+/NY+LtI8bsr+AswRA=
github.com/ugorji/go v1.1.8/go.mod h1:0lNM99SwWUIRhCXnigEMClngXBk/EmpTXa7mgiewYWA=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.8 h1:4dryPvxMP9OtkjIbuNeK2nb27M38XMHLGlfNSNph/5s=
github.com/ugorji/go/codec v1.1.8/go.mod h1:X00B19HDtwvKbQY2DcYjvZxKQp8mzrJoQ6EgoIY/D2E=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk=
@ -631,7 +622,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=
@ -646,6 +636,7 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
@ -655,8 +646,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
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=
@ -677,10 +668,8 @@ 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=
@ -746,9 +735,11 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
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/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=
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=
@ -775,15 +766,19 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw=
gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
@ -793,6 +788,7 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
@ -800,13 +796,17 @@ 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.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=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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=
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=

+ 5
- 3
test/historydb.go

@ -79,19 +79,21 @@ func GenBatches(nBatches int, blocks []common.Block) []common.Batch {
}
// GenAccounts generates accounts. WARNING: This is meant for DB/API testing, and may not be fully consistent with the protocol.
func GenAccounts(totalAccounts, userAccounts int, tokens []common.Token, userAddr *ethCommon.Address, batches []common.Batch) []common.Account {
func GenAccounts(totalAccounts, userAccounts int, tokens []common.Token, userAddr *ethCommon.Address, userBjj *babyjub.PublicKey, batches []common.Batch) []common.Account {
if totalAccounts < userAccounts {
panic("totalAccounts must be greater than userAccounts")
}
privK := babyjub.NewRandPrivKey()
pubK := privK.Public()
accs := []common.Account{}
for i := 0; i < totalAccounts; i++ {
var addr ethCommon.Address
var pubK *babyjub.PublicKey
if i < userAccounts {
addr = *userAddr
pubK = userBjj
} else {
addr = ethCommon.BigToAddress(big.NewInt(int64(i)))
privK := babyjub.NewRandPrivKey()
pubK = privK.Public()
}
accs = append(accs, common.Account{
Idx: common.Idx(i),

Loading…
Cancel
Save