mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-06 19:06:42 +01:00
Merge pull request #38 from hermeznetwork/feature/statedb
Add StateDB & LocalStateDB
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
196
common/account_test.go
Normal file
196
common/account_test.go
Normal file
@@ -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)
|
||||
}
|
||||
128
common/leaf.go
128
common/leaf.go
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
215
db/statedb/statedb.go
Normal file
215
db/statedb/statedb.go
Normal file
@@ -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
|
||||
}
|
||||
144
db/statedb/statedb_test.go
Normal file
144
db/statedb/statedb_test.go
Normal file
@@ -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)
|
||||
}
|
||||
4
go.mod
4
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
|
||||
)
|
||||
|
||||
9
go.sum
9
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=
|
||||
|
||||
Reference in New Issue
Block a user