arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/common/leaf.go
common/leaf.goRaw
  1package common
  2
  3import (
  4	"bytes"
  5	"encoding/binary"
  6	"fmt"
  7	"math"
  8	"math/big"
  9
 10	eth "github.com/ethereum/go-ethereum/common"
 11	"github.com/iden3/go-iden3-crypto/poseidon"
 12	cryptoUtils "github.com/iden3/go-iden3-crypto/utils"
 13)
 14
 15// Leaf is the data structure stored in the Leaf of the MerkleTree
 16type Leaf struct {
 17	TokenID TokenID
 18	Nonce   uint64   // max of 40 bits used
 19	Balance *big.Int // max of 192 bits used
 20	Ax      *big.Int
 21	Ay      *big.Int
 22	EthAddr eth.Address
 23}
 24
 25// 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.
 26func (l *Leaf) Bytes() ([32 * 5]byte, error) {
 27	var b [32 * 5]byte
 28
 29	if l.Nonce >= uint64(math.Pow(2, 40)) {
 30		return b, fmt.Errorf("%s Nonce", ErrNumOverflow)
 31	}
 32	if len(l.Balance.Bytes()) > 24 {
 33		return b, fmt.Errorf("%s Balance", ErrNumOverflow)
 34	}
 35
 36	var tokenIDBytes [4]byte
 37	binary.LittleEndian.PutUint32(tokenIDBytes[:], uint32(l.TokenID))
 38	var nonceBytes [8]byte
 39	binary.LittleEndian.PutUint64(nonceBytes[:], l.Nonce)
 40
 41	copy(b[0:4], tokenIDBytes[:])
 42	copy(b[4:9], nonceBytes[:])
 43	copy(b[32:64], SwapEndianness(l.Balance.Bytes())) // SwapEndianness, as big.Int uses BigEndian
 44	copy(b[64:96], SwapEndianness(l.Ax.Bytes()))
 45	copy(b[96:128], SwapEndianness(l.Ay.Bytes()))
 46	copy(b[128:148], l.EthAddr.Bytes())
 47
 48	return b, nil
 49}
 50
 51// BigInts returns the [5]*big.Int, where each *big.Int is inside the Finite Field
 52func (l *Leaf) BigInts() ([5]*big.Int, error) {
 53	e := [5]*big.Int{}
 54
 55	b, err := l.Bytes()
 56	if err != nil {
 57		return e, err
 58	}
 59
 60	e[0] = new(big.Int).SetBytes(SwapEndianness(b[0:32]))
 61	e[1] = new(big.Int).SetBytes(SwapEndianness(b[32:64]))
 62	e[2] = new(big.Int).SetBytes(SwapEndianness(b[64:96]))
 63	e[3] = new(big.Int).SetBytes(SwapEndianness(b[96:128]))
 64	e[4] = new(big.Int).SetBytes(SwapEndianness(b[128:160]))
 65
 66	return e, nil
 67}
 68
 69// Value returns the value of the Leaf, which is the Poseidon hash of its *big.Int representation
 70func (l *Leaf) Value() (*big.Int, error) {
 71	toHash := [poseidon.T]*big.Int{}
 72	lBI := l.BigInts()
 73	copy(toHash[:], lBI[:])
 74
 75	v, err := poseidon.Hash(toHash)
 76	return v, err
 77}
 78
 79// LeafFromBigInts returns a Leaf from a [5]*big.Int
 80func LeafFromBigInts(e [5]*big.Int) (*Leaf, error) {
 81	if !cryptoUtils.CheckBigIntArrayInField(e[:]) {
 82		return nil, ErrNotInFF
 83	}
 84	var b [32 * 5]byte
 85	copy(b[0:32], SwapEndianness(e[0].Bytes())) // SwapEndianness, as big.Int uses BigEndian
 86	copy(b[32:64], SwapEndianness(e[1].Bytes()))
 87	copy(b[64:96], SwapEndianness(e[2].Bytes()))
 88	copy(b[96:128], SwapEndianness(e[3].Bytes()))
 89	copy(b[128:160], SwapEndianness(e[4].Bytes()))
 90
 91	return LeafFromBytes(b)
 92}
 93
 94// LeafFromBytes returns a Leaf from a byte array
 95func LeafFromBytes(b [32 * 5]byte) (*Leaf, error) {
 96	tokenID := binary.LittleEndian.Uint32(b[0:4])
 97	nonce := binary.LittleEndian.Uint64(b[4:12])
 98	if !bytes.Equal(b[9:12], []byte{0, 0, 0}) { // alternatively: if nonce >= uint64(math.Pow(2, 40)) {
 99		return nil, fmt.Errorf("%s Nonce", ErrNumOverflow)
100	}
101	balance := new(big.Int).SetBytes(SwapEndianness(b[32:56])) // b[32:56], as Balance is 192 bits (24 bytes)
102	if !bytes.Equal(b[56:64], []byte{0, 0, 0, 0, 0, 0, 0, 0}) {
103		return nil, fmt.Errorf("%s Balance", ErrNumOverflow)
104	}
105	ax := new(big.Int).SetBytes(SwapEndianness(b[64:96])) // SwapEndianness, as big.Int uses BigEndian
106	ay := new(big.Int).SetBytes(SwapEndianness(b[96:128]))
107	ethAddr := eth.BytesToAddress(b[128:148])
108
109	if !cryptoUtils.CheckBigIntInField(balance) {
110		return nil, ErrNotInFF
111	}
112	if !cryptoUtils.CheckBigIntInField(ax) {
113		return nil, ErrNotInFF
114	}
115	if !cryptoUtils.CheckBigIntInField(ay) {
116		return nil, ErrNotInFF
117	}
118
119	l := Leaf{
120		TokenID: TokenID(tokenID),
121		Nonce:   nonce,
122		Balance: balance,
123		Ax:      ax,
124		Ay:      ay,
125		EthAddr: ethAddr,
126	}
127	return &l, nil
128}