diff --git a/batchbuilder/batchbuilder.go b/batchbuilder/batchbuilder.go index 23be0ea..8536882 100644 --- a/batchbuilder/batchbuilder.go +++ b/batchbuilder/batchbuilder.go @@ -3,7 +3,6 @@ package batchbuilder import ( ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" - "github.com/iden3/go-iden3-crypto/babyjub" "github.com/iden3/go-merkletree" "github.com/iden3/go-merkletree/db" "github.com/iden3/go-merkletree/db/memory" @@ -73,7 +72,7 @@ func (bb *BatchBuilder) BuildBatch(configBatch ConfigBatch, l1usertxs, l1coordin for _, tx := range l2txs { switch tx.Type { case common.TxTypeTransfer: - // go to the MT leaf of sender and receiver, and update + // go to the MT account of sender and receiver, and update // balance & nonce err := bb.applyTransfer(tx.Tx) if err != nil { @@ -91,33 +90,33 @@ func (bb *BatchBuilder) BuildBatch(configBatch ConfigBatch, l1usertxs, l1coordin func (bb *BatchBuilder) processL1Tx(tx common.L1Tx) error { switch tx.Type { case common.TxTypeForceTransfer, common.TxTypeTransfer: - // go to the MT leaf of sender and receiver, and update balance + // go to the MT account of sender and receiver, and update balance // & nonce err := bb.applyTransfer(tx.Tx) if err != nil { return err } case common.TxTypeCreateAccountDeposit: - // add new leaf to the MT, update balance of the MT leaf + // add new account to the MT, update balance of the MT account err := bb.applyCreateLeaf(tx) if err != nil { return err } - case common.TxTypeDeposit: - // update balance of the MT leaf + case common.TxTypeDeposit: // TODO check if this type will ever exist, or will be TxTypeDepositAndTransfer with transfer 0 value + // update balance of the MT account err := bb.applyDeposit(tx, false) if err != nil { return err } case common.TxTypeDepositAndTransfer: - // update balance in MT leaf, update balance & nonce of sender + // update balance in MT account, update balance & nonce of sender // & receiver err := bb.applyDeposit(tx, true) if err != nil { return err } case common.TxTypeCreateAccountDepositAndTransfer: - // add new leaf to the merkletree, update balance in MT leaf, + // add new account to the merkletree, update balance in MT account, // update balance & nonce of sender & receiver err := bb.applyCreateLeaf(tx) if err != nil { @@ -135,19 +134,18 @@ func (bb *BatchBuilder) processL1Tx(tx common.L1Tx) error { return nil } -// applyCreateLeaf creates a new leaf in the leaf of the depositer, it stores +// applyCreateLeaf creates a new account in the account of the depositer, it stores // the deposit value func (bb *BatchBuilder) applyCreateLeaf(tx common.L1Tx) error { - leaf := common.Leaf{ - TokenID: tx.TokenID, - Nonce: 0, // TODO check w spec: always that a new leaf is created nonce is at 0 - Balance: tx.LoadAmount, - Sign: babyjub.PointCoordSign(tx.FromBJJ.X), - Ay: tx.FromBJJ.Y, - EthAddr: tx.FromEthAddr, + account := common.Account{ + TokenID: tx.TokenID, + Nonce: 0, + Balance: tx.LoadAmount, + PublicKey: &tx.FromBJJ, + EthAddr: tx.FromEthAddr, } - v, err := leaf.HashValue() + v, err := account.HashValue() if err != nil { return err } @@ -156,15 +154,15 @@ func (bb *BatchBuilder) applyCreateLeaf(tx common.L1Tx) error { return err } - err = bb.CreateBalance(dbTx, common.Idx(bb.idx+1), leaf) + err = bb.CreateBalance(dbTx, common.Idx(bb.idx+1), account) if err != nil { return err } - leafBytes, err := leaf.Bytes() + accountBytes, err := account.Bytes() if err != nil { return err } - dbTx.Put(v.Bytes(), leafBytes[:]) + dbTx.Put(v.Bytes(), accountBytes[:]) // if everything is fine, do dbTx & increment idx if err := dbTx.Commit(); err != nil { @@ -174,7 +172,7 @@ func (bb *BatchBuilder) applyCreateLeaf(tx common.L1Tx) error { return nil } -// applyDeposit updates the balance in the leaf of the depositer, if andTransfer parameter is set to true, the method will also apply the Transfer of the L1Tx/DepositAndTransfer +// applyDeposit updates the balance in the account of the depositer, if andTransfer parameter is set to true, the method will also apply the Transfer of the L1Tx/DepositAndTransfer func (bb *BatchBuilder) applyDeposit(tx common.L1Tx, andTransfer bool) error { dbTx, err := bb.mt.DB().NewTx() if err != nil { @@ -206,8 +204,8 @@ func (bb *BatchBuilder) applyDeposit(tx common.L1Tx, andTransfer bool) error { return nil } -// applyTransfer updates the balance & nonce in the leaf of the sender, and the -// balance in the leaf of the receiver +// applyTransfer updates the balance & nonce in the account of the sender, and the +// balance in the account of the receiver func (bb *BatchBuilder) applyTransfer(tx common.Tx) error { dbTx, err := bb.mt.DB().NewTx() if err != nil { diff --git a/batchbuilder/state.go b/batchbuilder/state.go index be6ca63..a395376 100644 --- a/batchbuilder/state.go +++ b/batchbuilder/state.go @@ -10,7 +10,7 @@ import ( // TODO next iteration move the methods of this file into StateDB, which Synchronizer will use in the disk DB, and BatchBuilder will use with the MemoryDB // GetBalance returns the balance for a given Idx from the DB -func (bb *BatchBuilder) GetBalance(tx db.Tx, idx common.Idx) (*common.Leaf, error) { +func (bb *BatchBuilder) GetBalance(tx db.Tx, idx common.Idx) (*common.Account, error) { idxBytes := idx.Bytes() vBytes, err := tx.Get(idxBytes[:]) if err != nil { @@ -18,15 +18,15 @@ func (bb *BatchBuilder) GetBalance(tx db.Tx, idx common.Idx) (*common.Leaf, erro } var b [32 * common.NLEAFELEMS]byte copy(b[:], vBytes) - leaf, err := common.LeafFromBytes(b) + leaf, err := common.AccountFromBytes(b) if err != nil { return nil, err } return leaf, nil } -// CreateBalance stores the Leaf into the Idx position in the MerkleTree, also adds db entry for the Leaf value -func (bb *BatchBuilder) CreateBalance(tx db.Tx, idx common.Idx, leaf common.Leaf) error { +// CreateBalance stores the Account into the Idx position in the MerkleTree, also adds db entry for the Account value +func (bb *BatchBuilder) CreateBalance(tx db.Tx, idx common.Idx, leaf common.Account) error { // store at the DB the key: v, and value: leaf.Bytes() v, err := leaf.HashValue() if err != nil { @@ -37,7 +37,7 @@ func (bb *BatchBuilder) CreateBalance(tx db.Tx, idx common.Idx, leaf common.Leaf return err } - // store the Leaf value + // store the Account value tx.Put(v.Bytes(), leafBytes[:]) // Add k & v into the MT err = bb.mt.Add(idx.BigInt(), v) @@ -73,10 +73,10 @@ func (bb *BatchBuilder) UpdateBalance(tx db.Tx, idx common.Idx, amount *big.Int, return err } - // store the Leaf value + // store the Account value tx.Put(v.Bytes(), leafBytes[:]) // Add k & v into the MT - err = bb.mt.Update(idx.BigInt(), v) + _, err = bb.mt.Update(idx.BigInt(), v) if err != nil { return err } diff --git a/common/account.go b/common/account.go index 652a4ac..6e64a44 100644 --- a/common/account.go +++ b/common/account.go @@ -1,18 +1,133 @@ package common import ( + "bytes" + "encoding/binary" + "fmt" "math/big" eth "github.com/ethereum/go-ethereum/common" "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/iden3/go-iden3-crypto/poseidon" + cryptoUtils "github.com/iden3/go-iden3-crypto/utils" ) -// Account is a struct that gives information of the holdings of an address for a specific token +const NLEAFELEMS = 4 + +// Account is a struct that gives information of the holdings of an address and a specific token. Is the data structure that generates the Value stored in the leaf of the MerkleTree type Account struct { + TokenID TokenID + Nonce uint64 // max of 40 bits used + Balance *big.Int // max of 192 bits used + PublicKey *babyjub.PublicKey EthAddr eth.Address - TokenID TokenID // effective 32 bits - Idx uint32 // bits = SMT levels (SMT levels needs to be decided) - Nonce uint64 // effective 48 bits - Balance *big.Int // Up to 192 bits - PublicKey babyjub.PublicKey +} + +// Bytes returns the bytes representing the Account, in a way that each BigInt is represented by 32 bytes, in spite of the BigInt could be represented in less bytes (due a small big.Int), so in this way each BigInt is always 32 bytes and can be automatically parsed from a byte array. +func (l *Account) Bytes() ([32 * NLEAFELEMS]byte, error) { + var b [32 * NLEAFELEMS]byte + + if l.Nonce > 0xffffffffff { + return b, fmt.Errorf("%s Nonce", ErrNumOverflow) + } + if len(l.Balance.Bytes()) > 24 { + return b, fmt.Errorf("%s Balance", ErrNumOverflow) + } + + var tokenIDBytes [4]byte + binary.LittleEndian.PutUint32(tokenIDBytes[:], uint32(l.TokenID)) + var nonceBytes [8]byte + binary.LittleEndian.PutUint64(nonceBytes[:], l.Nonce) + + copy(b[0:4], tokenIDBytes[:]) + copy(b[4:9], nonceBytes[:]) + if babyjub.PointCoordSign(l.PublicKey.X) { + b[10] = 1 + } + copy(b[32:64], SwapEndianness(l.Balance.Bytes())) // SwapEndianness, as big.Int uses BigEndian + copy(b[64:96], SwapEndianness(l.PublicKey.Y.Bytes())) + copy(b[96:116], l.EthAddr.Bytes()) + + return b, nil +} + +// BigInts returns the [5]*big.Int, where each *big.Int is inside the Finite Field +func (l *Account) BigInts() ([NLEAFELEMS]*big.Int, error) { + e := [NLEAFELEMS]*big.Int{} + + b, err := l.Bytes() + if err != nil { + return e, err + } + + e[0] = new(big.Int).SetBytes(SwapEndianness(b[0:32])) + e[1] = new(big.Int).SetBytes(SwapEndianness(b[32:64])) + e[2] = new(big.Int).SetBytes(SwapEndianness(b[64:96])) + e[3] = new(big.Int).SetBytes(SwapEndianness(b[96:128])) + + return e, nil +} + +// HashValue returns the value of the Account, which is the Poseidon hash of its *big.Int representation +func (l *Account) HashValue() (*big.Int, error) { + b0 := big.NewInt(0) + toHash := [poseidon.T]*big.Int{b0, b0, b0, b0, b0, b0} + lBI, err := l.BigInts() + if err != nil { + return nil, err + } + copy(toHash[:], lBI[:]) + + v, err := poseidon.Hash(toHash) + return v, err +} + +// AccountFromBigInts returns a Account from a [5]*big.Int +func AccountFromBigInts(e [NLEAFELEMS]*big.Int) (*Account, error) { + if !cryptoUtils.CheckBigIntArrayInField(e[:]) { + return nil, ErrNotInFF + } + var b [32 * NLEAFELEMS]byte + copy(b[0:32], SwapEndianness(e[0].Bytes())) // SwapEndianness, as big.Int uses BigEndian + copy(b[32:64], SwapEndianness(e[1].Bytes())) + copy(b[64:96], SwapEndianness(e[2].Bytes())) + copy(b[96:128], SwapEndianness(e[3].Bytes())) + + return AccountFromBytes(b) +} + +// AccountFromBytes returns a Account from a byte array +func AccountFromBytes(b [32 * NLEAFELEMS]byte) (*Account, error) { + tokenID := binary.LittleEndian.Uint32(b[0:4]) + var nonceBytes [8]byte + copy(nonceBytes[:], b[4:9]) + nonce := binary.LittleEndian.Uint64(nonceBytes[:]) + sign := b[10] == 1 + balance := new(big.Int).SetBytes(SwapEndianness(b[32:56])) // b[32:56], as Balance is 192 bits (24 bytes) + if !bytes.Equal(b[56:64], []byte{0, 0, 0, 0, 0, 0, 0, 0}) { + return nil, fmt.Errorf("%s Balance", ErrNumOverflow) + } + ay := new(big.Int).SetBytes(SwapEndianness(b[64:96])) + pkPoint, err := babyjub.PointFromSignAndY(sign, ay) + if err != nil { + return nil, err + } + publicKey := babyjub.PublicKey(*pkPoint) + ethAddr := eth.BytesToAddress(b[96:116]) + + if !cryptoUtils.CheckBigIntInField(balance) { + return nil, ErrNotInFF + } + if !cryptoUtils.CheckBigIntInField(ay) { + return nil, ErrNotInFF + } + + l := Account{ + TokenID: TokenID(tokenID), + Nonce: nonce, + Balance: balance, + PublicKey: &publicKey, + EthAddr: ethAddr, + } + return &l, nil } diff --git a/common/account_test.go b/common/account_test.go new file mode 100644 index 0000000..cf6b36b --- /dev/null +++ b/common/account_test.go @@ -0,0 +1,196 @@ +package common + +import ( + "encoding/hex" + "fmt" + "math" + "math/big" + "testing" + + ethCommon "github.com/ethereum/go-ethereum/common" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/iden3/go-iden3-crypto/babyjub" + cryptoConstants "github.com/iden3/go-iden3-crypto/constants" + cryptoUtils "github.com/iden3/go-iden3-crypto/utils" + "github.com/stretchr/testify/assert" +) + +func TestAccount(t *testing.T) { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.Nil(t, err) + pk := sk.Public() + + account := &Account{ + TokenID: TokenID(1), + Nonce: uint64(1234), + Balance: big.NewInt(1000), + PublicKey: pk, + EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), + } + b, err := account.Bytes() + assert.Nil(t, err) + assert.Equal(t, byte(1), b[10]) + a1, err := AccountFromBytes(b) + assert.Nil(t, err) + assert.Equal(t, account, a1) + + e, err := account.BigInts() + assert.Nil(t, err) + assert.True(t, cryptoUtils.CheckBigIntInField(e[0])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[1])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[2])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[3])) + + assert.Equal(t, "1000", e[1].String()) + assert.Equal(t, pk.Y.String(), e[2].String()) + assert.Equal(t, new(big.Int).SetBytes(SwapEndianness(account.EthAddr.Bytes())).String(), e[3].String()) + + a2, err := AccountFromBigInts(e) + assert.Nil(t, err) + assert.Equal(t, account, a2) + assert.Equal(t, a1, a2) +} + +func TestAccountLoop(t *testing.T) { + // check that for different Address there is no problem + for i := 0; i < 256; i++ { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.Nil(t, err) + pk := sk.Public() + + key, err := ethCrypto.GenerateKey() + assert.Nil(t, err) + address := ethCrypto.PubkeyToAddress(key.PublicKey) + + account := &Account{ + TokenID: TokenID(i), + Nonce: uint64(i), + Balance: big.NewInt(1000), + PublicKey: pk, + EthAddr: address, + } + b, err := account.Bytes() + assert.Nil(t, err) + a1, err := AccountFromBytes(b) + assert.Nil(t, err) + assert.Equal(t, account, a1) + + e, err := account.BigInts() + assert.Nil(t, err) + assert.True(t, cryptoUtils.CheckBigIntInField(e[0])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[1])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[2])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[3])) + + a2, err := AccountFromBigInts(e) + assert.Nil(t, err) + assert.Equal(t, account, a2) + } +} + +func TestAccountHashValue(t *testing.T) { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.Nil(t, err) + pk := sk.Public() + + account := &Account{ + TokenID: TokenID(1), + Nonce: uint64(1234), + Balance: big.NewInt(1000), + PublicKey: pk, + EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), + } + + v, err := account.HashValue() + assert.Nil(t, err) + assert.Equal(t, "6335844662301214382338419199835935731871537354006112711277201708185593574314", v.String()) +} + +func TestAccountErrNotInFF(t *testing.T) { + z := big.NewInt(0) + + // Q-1 should not give error + r := new(big.Int).Sub(cryptoConstants.Q, big.NewInt(1)) + e := [NLEAFELEMS]*big.Int{z, z, r, r} + _, err := AccountFromBigInts(e) + assert.Nil(t, err) + + // Q should give error + r = cryptoConstants.Q + e = [NLEAFELEMS]*big.Int{z, z, r, r} + _, err = AccountFromBigInts(e) + assert.NotNil(t, err) + assert.Equal(t, ErrNotInFF, err) + + // Q+1 should give error + r = new(big.Int).Add(cryptoConstants.Q, big.NewInt(1)) + e = [NLEAFELEMS]*big.Int{z, z, r, r} + _, err = AccountFromBigInts(e) + assert.NotNil(t, err) + assert.Equal(t, ErrNotInFF, err) +} + +func TestAccountErrNumOverflowNonce(t *testing.T) { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.Nil(t, err) + pk := sk.Public() + + // check limit + account := &Account{ + TokenID: TokenID(1), + Nonce: uint64(math.Pow(2, 40) - 1), + Balance: big.NewInt(1000), + PublicKey: pk, + EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), + } + _, err = account.Bytes() + assert.Nil(t, err) + + // force value overflow + account.Nonce = uint64(math.Pow(2, 40)) + b, err := account.Bytes() + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("%s Nonce", ErrNumOverflow), err) + + _, err = AccountFromBytes(b) + assert.Nil(t, err) +} + +func TestAccountErrNumOverflowBalance(t *testing.T) { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.Nil(t, err) + pk := sk.Public() + + // check limit + account := &Account{ + TokenID: TokenID(1), + Nonce: uint64(math.Pow(2, 40) - 1), + Balance: new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(192), nil), big.NewInt(1)), + PublicKey: pk, + EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), + } + assert.Equal(t, "6277101735386680763835789423207666416102355444464034512895", account.Balance.String()) + + _, err = account.Bytes() + assert.Nil(t, err) + + // force value overflow + account.Balance = new(big.Int).Exp(big.NewInt(2), big.NewInt(192), nil) + assert.Equal(t, "6277101735386680763835789423207666416102355444464034512896", account.Balance.String()) + b, err := account.Bytes() + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("%s Balance", ErrNumOverflow), err) + + _, err = AccountFromBytes(b) + assert.Nil(t, err) + + b[56] = 1 + _, err = AccountFromBytes(b) + assert.NotNil(t, err) + assert.Equal(t, fmt.Errorf("%s Balance", ErrNumOverflow), err) +} diff --git a/common/leaf.go b/common/leaf.go deleted file mode 100644 index 156c750..0000000 --- a/common/leaf.go +++ /dev/null @@ -1,128 +0,0 @@ -package common - -import ( - "bytes" - "encoding/binary" - "fmt" - "math/big" - - eth "github.com/ethereum/go-ethereum/common" - "github.com/iden3/go-iden3-crypto/poseidon" - cryptoUtils "github.com/iden3/go-iden3-crypto/utils" -) - -const NLEAFELEMS = 4 - -// Leaf is the data structure stored in the Leaf of the MerkleTree -type Leaf struct { - TokenID TokenID - Nonce uint64 // max of 40 bits used - Balance *big.Int // max of 192 bits used - Sign bool - Ay *big.Int - EthAddr eth.Address -} - -// Bytes returns the bytes representing the Leaf, in a way that each BigInt is represented by 32 bytes, in spite of the BigInt could be represented in less bytes (due a small big.Int), so in this way each BigInt is always 32 bytes and can be automatically parsed from a byte array. -func (l *Leaf) Bytes() ([32 * NLEAFELEMS]byte, error) { - var b [32 * NLEAFELEMS]byte - - if l.Nonce > 0xffffffffff { - return b, fmt.Errorf("%s Nonce", ErrNumOverflow) - } - if len(l.Balance.Bytes()) > 24 { - return b, fmt.Errorf("%s Balance", ErrNumOverflow) - } - - var tokenIDBytes [4]byte - binary.LittleEndian.PutUint32(tokenIDBytes[:], uint32(l.TokenID)) - var nonceBytes [8]byte - binary.LittleEndian.PutUint64(nonceBytes[:], l.Nonce) - - copy(b[0:4], tokenIDBytes[:]) - copy(b[4:9], nonceBytes[:]) - if l.Sign { - b[10] = 1 - } - copy(b[32:64], SwapEndianness(l.Balance.Bytes())) // SwapEndianness, as big.Int uses BigEndian - copy(b[64:96], SwapEndianness(l.Ay.Bytes())) - copy(b[96:116], l.EthAddr.Bytes()) - - return b, nil -} - -// BigInts returns the [5]*big.Int, where each *big.Int is inside the Finite Field -func (l *Leaf) BigInts() ([NLEAFELEMS]*big.Int, error) { - e := [NLEAFELEMS]*big.Int{} - - b, err := l.Bytes() - if err != nil { - return e, err - } - - e[0] = new(big.Int).SetBytes(SwapEndianness(b[0:32])) - e[1] = new(big.Int).SetBytes(SwapEndianness(b[32:64])) - e[2] = new(big.Int).SetBytes(SwapEndianness(b[64:96])) - e[3] = new(big.Int).SetBytes(SwapEndianness(b[96:128])) - - return e, nil -} - -// HashValue returns the value of the Leaf, which is the Poseidon hash of its *big.Int representation -func (l *Leaf) HashValue() (*big.Int, error) { - toHash := [poseidon.T]*big.Int{} - lBI, err := l.BigInts() - if err != nil { - return nil, err - } - copy(toHash[:], lBI[:]) - - v, err := poseidon.Hash(toHash) - return v, err -} - -// LeafFromBigInts returns a Leaf from a [5]*big.Int -func LeafFromBigInts(e [NLEAFELEMS]*big.Int) (*Leaf, error) { - if !cryptoUtils.CheckBigIntArrayInField(e[:]) { - return nil, ErrNotInFF - } - var b [32 * NLEAFELEMS]byte - copy(b[0:32], SwapEndianness(e[0].Bytes())) // SwapEndianness, as big.Int uses BigEndian - copy(b[32:64], SwapEndianness(e[1].Bytes())) - copy(b[64:96], SwapEndianness(e[2].Bytes())) - copy(b[96:128], SwapEndianness(e[3].Bytes())) - - return LeafFromBytes(b) -} - -// LeafFromBytes returns a Leaf from a byte array -func LeafFromBytes(b [32 * NLEAFELEMS]byte) (*Leaf, error) { - tokenID := binary.LittleEndian.Uint32(b[0:4]) - var nonceBytes [8]byte - copy(nonceBytes[:], b[4:9]) - nonce := binary.LittleEndian.Uint64(nonceBytes[:]) - sign := b[10] == 1 - balance := new(big.Int).SetBytes(SwapEndianness(b[32:56])) // b[32:56], as Balance is 192 bits (24 bytes) - if !bytes.Equal(b[56:64], []byte{0, 0, 0, 0, 0, 0, 0, 0}) { - return nil, fmt.Errorf("%s Balance", ErrNumOverflow) - } - ay := new(big.Int).SetBytes(SwapEndianness(b[64:96])) - ethAddr := eth.BytesToAddress(b[96:116]) - - if !cryptoUtils.CheckBigIntInField(balance) { - return nil, ErrNotInFF - } - if !cryptoUtils.CheckBigIntInField(ay) { - return nil, ErrNotInFF - } - - l := Leaf{ - TokenID: TokenID(tokenID), - Nonce: nonce, - Balance: balance, - Sign: sign, - Ay: ay, - EthAddr: ethAddr, - } - return &l, nil -} diff --git a/common/leaf_test.go b/common/leaf_test.go deleted file mode 100644 index d51b1ab..0000000 --- a/common/leaf_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package common - -import ( - "fmt" - "math" - "math/big" - "testing" - - ethCommon "github.com/ethereum/go-ethereum/common" - cryptoConstants "github.com/iden3/go-iden3-crypto/constants" - cryptoUtils "github.com/iden3/go-iden3-crypto/utils" - "github.com/stretchr/testify/assert" -) - -func TestLeaf(t *testing.T) { - leaf := &Leaf{ - TokenID: TokenID(1), - Nonce: uint64(1234), - Balance: big.NewInt(1000), - Sign: true, - Ay: big.NewInt(6789), - EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), - } - b, err := leaf.Bytes() - assert.Nil(t, err) - assert.Equal(t, byte(1), b[10]) - l1, err := LeafFromBytes(b) - assert.Nil(t, err) - assert.Equal(t, leaf, l1) - - e, err := leaf.BigInts() - assert.Nil(t, err) - assert.True(t, cryptoUtils.CheckBigIntInField(e[0])) - assert.True(t, cryptoUtils.CheckBigIntInField(e[1])) - assert.True(t, cryptoUtils.CheckBigIntInField(e[2])) - assert.True(t, cryptoUtils.CheckBigIntInField(e[3])) - - assert.Equal(t, "1000", e[1].String()) - assert.Equal(t, "6789", e[2].String()) - assert.Equal(t, new(big.Int).SetBytes(SwapEndianness(leaf.EthAddr.Bytes())).String(), e[3].String()) - - l2, err := LeafFromBigInts(e) - assert.Nil(t, err) - assert.Equal(t, leaf, l2) - assert.Equal(t, l1, l2) -} - -// func TestLeafLoop(t *testing.T) { -// // check that for different Address there is no problem -// for i := 0; i < 256; i++ { -// key, err := ethCrypto.GenerateKey() -// assert.Nil(t, err) -// address := ethCrypto.PubkeyToAddress(key.PublicKey) -// -// leaf := &Leaf{ -// TokenID: TokenID(i), -// Nonce: uint64(i), -// Balance: big.NewInt(1000), -// Sign: true, -// Ay: big.NewInt(6789), -// EthAddr: address, -// } -// b, err := leaf.Bytes() -// assert.Nil(t, err) -// l1, err := LeafFromBytes(b) -// assert.Nil(t, err) -// assert.Equal(t, leaf, l1) -// -// e, err := leaf.BigInts() -// assert.Nil(t, err) -// assert.True(t, cryptoUtils.CheckBigIntInField(e[0])) -// assert.True(t, cryptoUtils.CheckBigIntInField(e[1])) -// assert.True(t, cryptoUtils.CheckBigIntInField(e[2])) -// assert.True(t, cryptoUtils.CheckBigIntInField(e[3])) -// -// l2, err := LeafFromBigInts(e) -// assert.Nil(t, err) -// assert.Equal(t, leaf, l2) -// } -// } - -func TestLeafErrNotInFF(t *testing.T) { - z := big.NewInt(0) - - // Q-1 should not give error - r := new(big.Int).Sub(cryptoConstants.Q, big.NewInt(1)) - e := [NLEAFELEMS]*big.Int{z, z, r, r} - _, err := LeafFromBigInts(e) - assert.Nil(t, err) - - // Q should give error - r = cryptoConstants.Q - e = [NLEAFELEMS]*big.Int{z, z, r, r} - _, err = LeafFromBigInts(e) - assert.NotNil(t, err) - assert.Equal(t, ErrNotInFF, err) - - // Q+1 should give error - r = new(big.Int).Add(cryptoConstants.Q, big.NewInt(1)) - e = [NLEAFELEMS]*big.Int{z, z, r, r} - _, err = LeafFromBigInts(e) - assert.NotNil(t, err) - assert.Equal(t, ErrNotInFF, err) -} - -func TestLeafErrNumOverflowNonce(t *testing.T) { - // check limit - leaf := &Leaf{ - TokenID: TokenID(1), - Nonce: uint64(math.Pow(2, 40) - 1), - Balance: big.NewInt(1000), - Sign: true, - Ay: big.NewInt(6789), - EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), - } - _, err := leaf.Bytes() - assert.Nil(t, err) - - // force value overflow - leaf.Nonce = uint64(math.Pow(2, 40)) - b, err := leaf.Bytes() - assert.NotNil(t, err) - assert.Equal(t, fmt.Errorf("%s Nonce", ErrNumOverflow), err) - - _, err = LeafFromBytes(b) - assert.Nil(t, err) -} - -func TestLeafErrNumOverflowBalance(t *testing.T) { - // check limit - leaf := &Leaf{ - TokenID: TokenID(1), - Nonce: uint64(math.Pow(2, 40) - 1), - Balance: new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(192), nil), big.NewInt(1)), - Sign: true, - Ay: big.NewInt(6789), - EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), - } - assert.Equal(t, "6277101735386680763835789423207666416102355444464034512895", leaf.Balance.String()) - - _, err := leaf.Bytes() - assert.Nil(t, err) - - // force value overflow - leaf.Balance = new(big.Int).Exp(big.NewInt(2), big.NewInt(192), nil) - assert.Equal(t, "6277101735386680763835789423207666416102355444464034512896", leaf.Balance.String()) - b, err := leaf.Bytes() - assert.NotNil(t, err) - assert.Equal(t, fmt.Errorf("%s Balance", ErrNumOverflow), err) - - _, err = LeafFromBytes(b) - assert.Nil(t, err) - - b[56] = 1 - _, err = LeafFromBytes(b) - assert.NotNil(t, err) - assert.Equal(t, fmt.Errorf("%s Balance", ErrNumOverflow), err) -} diff --git a/db/statedb/statedb.go b/db/statedb/statedb.go new file mode 100644 index 0000000..1718bf7 --- /dev/null +++ b/db/statedb/statedb.go @@ -0,0 +1,215 @@ +package statedb + +import ( + "errors" + + "github.com/hermeznetwork/hermez-node/common" + "github.com/iden3/go-merkletree" + "github.com/iden3/go-merkletree/db" + "github.com/iden3/go-merkletree/db/leveldb" + "github.com/iden3/go-merkletree/db/memory" +) + +// ErrStateDBWithoutMT is used when a method that requires a MerkleTree is called in a StateDB that does not have a MerkleTree defined +var ErrStateDBWithoutMT = errors.New("Can not call method to use MerkleTree in a StateDB without MerkleTree") + +// ErrAccountAlreadyExists is used when CreateAccount is called and the Account already exists +var ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists") + +// StateDB represents the StateDB object +type StateDB struct { + db db.Storage + mt *merkletree.MerkleTree +} + +// NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk +// storage +func NewStateDB(path string, inDisk bool, withMT bool, nLevels int) (*StateDB, error) { + var sto db.Storage + var err error + if inDisk { + sto, err = leveldb.NewLevelDbStorage(path, false) + if err != nil { + return nil, err + } + } else { + sto = memory.NewMemoryStorage() + } + var mt *merkletree.MerkleTree = nil + if withMT { + mt, err = merkletree.NewMerkleTree(sto, nLevels) + if err != nil { + return nil, err + } + } + + return &StateDB{ + db: sto, + mt: mt, + }, nil +} + +// CheckPointAt does a checkpoint at the given batchNum in the defined path +func (s *StateDB) CheckPointAt(batchNum int, path string) error { + // TODO + + return nil +} + +// Reset resets the StateDB to the checkpoint at the given batchNum +func (s *StateDB) Reset(batchNum int) error { + // TODO + + return nil +} + +// Checkpoints returns a list of the checkpoints (batchNums) +func (s *StateDB) Checkpoints() ([]int, error) { + // TODO + + //batchnums, err + return nil, nil +} + +// GetAccount returns the account for the given Idx +func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) { + vBytes, err := s.db.Get(idx.Bytes()) + if err != nil { + return nil, err + } + accBytes, err := s.db.Get(vBytes) + if err != nil { + return nil, err + } + var b [32 * common.NLEAFELEMS]byte + copy(b[:], accBytes) + return common.AccountFromBytes(b) +} + +// CreateAccount creates a new Account in the StateDB for the given Idx. +// MerkleTree is not affected. +func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) error { + // store at the DB the key: v, and value: leaf.Bytes() + v, err := account.HashValue() + if err != nil { + return err + } + accountBytes, err := account.Bytes() + if err != nil { + return err + } + + // store the Leaf value + tx, err := s.db.NewTx() + if err != nil { + return err + } + + _, err = tx.Get(idx.Bytes()) + if err != db.ErrNotFound { + return ErrAccountAlreadyExists + } + + tx.Put(v.Bytes(), accountBytes[:]) + tx.Put(idx.Bytes(), v.Bytes()) + + return tx.Commit() +} + +// UpdateAccount updates the Account in the StateDB for the given Idx. +// MerkleTree is not affected. +func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) error { + // store at the DB the key: v, and value: leaf.Bytes() + v, err := account.HashValue() + if err != nil { + return err + } + accountBytes, err := account.Bytes() + if err != nil { + return err + } + + tx, err := s.db.NewTx() + if err != nil { + return err + } + tx.Put(v.Bytes(), accountBytes[:]) + tx.Put(idx.Bytes(), v.Bytes()) + + return tx.Commit() +} + +// MTCreateAccount creates a new Account in the StateDB for the given Idx, +// and updates the MerkleTree, returning a CircomProcessorProof +func (s *StateDB) MTCreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { + if s.mt == nil { + return nil, ErrStateDBWithoutMT + } + err := s.CreateAccount(idx, account) + if err != nil { + return nil, err + } + + v, err := account.HashValue() // already computed in s.CreateAccount, next iteration reuse first computation + if err != nil { + return nil, err + } + // Add k & v into the MT + return s.mt.AddAndGetCircomProof(idx.BigInt(), v) +} + +// MTUpdateAccount updates the Account in the StateDB for the given Idx, and +// updates the MerkleTree, returning a CircomProcessorProof +func (s *StateDB) MTUpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) { + if s.mt == nil { + return nil, ErrStateDBWithoutMT + } + err := s.UpdateAccount(idx, account) + if err != nil { + return nil, err + } + + v, err := account.HashValue() // already computed in s.CreateAccount, next iteration reuse first computation + if err != nil { + return nil, err + } + // Add k & v into the MT + return s.mt.Update(idx.BigInt(), v) +} + +// MTGetProof returns the CircomVerifierProof for a given Idx +func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) { + if s.mt == nil { + return nil, ErrStateDBWithoutMT + } + return s.mt.GenerateCircomVerifierProof(idx.BigInt(), s.mt.Root()) +} + +// LocalStateDB represents the local StateDB which allows to make copies from +// the synchronizer StateDB, and is used by the tx-selector and the +// batch-builder. LocalStateDB is an in-memory storage. +type LocalStateDB struct { + *StateDB + synchronizerStateDB *StateDB +} + +// NewLocalStateDB returns a new LocalStateDB connected to the given +// synchronizerDB +func NewLocalStateDB(synchronizerDB *StateDB, withMT bool, nLevels int) (*LocalStateDB, error) { + s, err := NewStateDB("", false, withMT, nLevels) + if err != nil { + return nil, err + } + return &LocalStateDB{ + s, + synchronizerDB, + }, nil +} + +// Reset performs a reset, getting the state from +// LocalStateDB.synchronizerStateDB for the given batchNum +func (l *LocalStateDB) Reset(batchNum int, fromSynchronizer bool) error { + // TODO + + return nil +} diff --git a/db/statedb/statedb_test.go b/db/statedb/statedb_test.go new file mode 100644 index 0000000..b99ce55 --- /dev/null +++ b/db/statedb/statedb_test.go @@ -0,0 +1,144 @@ +package statedb + +import ( + "encoding/hex" + "io/ioutil" + "math/big" + "testing" + + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/hermeznetwork/hermez-node/common" + "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/iden3/go-merkletree/db" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newAccount(t *testing.T, i int) *common.Account { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + require.Nil(t, err) + pk := sk.Public() + + key, err := ethCrypto.GenerateKey() + require.Nil(t, err) + address := ethCrypto.PubkeyToAddress(key.PublicKey) + + return &common.Account{ + TokenID: common.TokenID(i), + Nonce: uint64(i), + Balance: big.NewInt(1000), + PublicKey: pk, + EthAddr: address, + } + +} + +func TestStateDBWithoutMT(t *testing.T) { + dir, err := ioutil.TempDir("", "tmpdb") + require.Nil(t, err) + + sdb, err := NewStateDB(dir, false, false, 0) + assert.Nil(t, err) + + // create test accounts + var accounts []*common.Account + for i := 0; i < 100; i++ { + accounts = append(accounts, newAccount(t, i)) + } + + // get non-existing account, expecting an error + _, err = sdb.GetAccount(common.Idx(1)) + assert.NotNil(t, err) + assert.Equal(t, db.ErrNotFound, err) + + // add test accounts + for i := 0; i < len(accounts); i++ { + err = sdb.CreateAccount(common.Idx(i), accounts[i]) + assert.Nil(t, err) + } + + for i := 0; i < len(accounts); i++ { + accGetted, err := sdb.GetAccount(common.Idx(i)) + assert.Nil(t, err) + assert.Equal(t, accounts[i], accGetted) + } + + // try already existing idx and get error + _, err = sdb.GetAccount(common.Idx(1)) // check that exist + assert.Nil(t, err) + err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice + assert.NotNil(t, err) + assert.Equal(t, ErrAccountAlreadyExists, err) + + // update accounts + for i := 0; i < len(accounts); i++ { + accounts[i].Nonce = accounts[i].Nonce + 1 + err = sdb.UpdateAccount(common.Idx(i), accounts[i]) + assert.Nil(t, err) + } + + // check that can not call MerkleTree methods of the StateDB + _, err = sdb.MTCreateAccount(common.Idx(1), accounts[1]) + assert.NotNil(t, err) + assert.Equal(t, ErrStateDBWithoutMT, err) + + _, err = sdb.MTUpdateAccount(common.Idx(1), accounts[1]) + assert.NotNil(t, err) + assert.Equal(t, ErrStateDBWithoutMT, err) + + _, err = sdb.MTGetProof(common.Idx(1)) + assert.NotNil(t, err) + assert.Equal(t, ErrStateDBWithoutMT, err) +} + +func TestStateDBWithMT(t *testing.T) { + dir, err := ioutil.TempDir("", "tmpdb") + require.Nil(t, err) + + sdb, err := NewStateDB(dir, false, true, 32) + assert.Nil(t, err) + + // create test accounts + var accounts []*common.Account + for i := 0; i < 100; i++ { + accounts = append(accounts, newAccount(t, i)) + } + + // get non-existing account, expecting an error + _, err = sdb.GetAccount(common.Idx(1)) + assert.NotNil(t, err) + assert.Equal(t, db.ErrNotFound, err) + + // add test accounts + for i := 0; i < len(accounts); i++ { + _, err = sdb.MTCreateAccount(common.Idx(i), accounts[i]) + assert.Nil(t, err) + } + + for i := 0; i < len(accounts); i++ { + accGetted, err := sdb.GetAccount(common.Idx(i)) + assert.Nil(t, err) + assert.Equal(t, accounts[i], accGetted) + } + + // try already existing idx and get error + _, err = sdb.GetAccount(common.Idx(1)) // check that exist + assert.Nil(t, err) + _, err = sdb.MTCreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice + assert.NotNil(t, err) + assert.Equal(t, ErrAccountAlreadyExists, err) + + _, err = sdb.MTGetProof(common.Idx(1)) + assert.Nil(t, err) + + // update accounts + for i := 0; i < len(accounts); i++ { + accounts[i].Nonce = accounts[i].Nonce + 1 + _, err = sdb.MTUpdateAccount(common.Idx(i), accounts[i]) + assert.Nil(t, err) + } + a, err := sdb.GetAccount(common.Idx(1)) // check that account value has been updated + assert.Nil(t, err) + assert.Equal(t, accounts[1].Nonce, a.Nonce) +} diff --git a/go.mod b/go.mod index 584e13f..a84ff67 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.14 require ( github.com/dghubble/sling v1.3.0 github.com/ethereum/go-ethereum v1.9.17 - github.com/iden3/go-iden3-crypto v0.0.6-0.20200723082457-29a66457f0bf - github.com/iden3/go-merkletree v0.0.0-20200723202738-75e24244a1e3 + github.com/iden3/go-iden3-crypto v0.0.6-0.20200806115047-327a8175d6eb + github.com/iden3/go-merkletree v0.0.0-20200807083900-f6f82d8375d5 github.com/jinzhu/gorm v1.9.15 github.com/stretchr/testify v1.6.1 ) diff --git a/go.sum b/go.sum index 977471c..c78701d 100644 --- a/go.sum +++ b/go.sum @@ -109,6 +109,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26 h1:lMm2hD9Fy0ynom5+85/pbdkiYcBqM1JWmhpAXLmy0fw= github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -135,8 +136,14 @@ github.com/iden3/go-iden3-crypto v0.0.5 h1:inCSm5a+ry+nbpVTL/9+m6UcIwSv6nhUm0tnI 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-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-wasm3 v0.0.1/go.mod h1:j+TcAB94Dfrjlu5kJt83h2OqAU+oyNUTwNZnQyII1sI= github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/influxdata/influxdb v1.7.8/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= @@ -225,6 +232,7 @@ github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAm github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= +github.com/sirupsen/logrus v1.5.0 h1:1N5EYkVAPEywqZRJd7cwnRtCb6xJx7NH3T3WUTF980Q= github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -241,6 +249,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= 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/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=