mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 11:26:44 +01:00
Add TxCompressedData & V2 with some extra parsers for common
This commit is contained in:
@@ -23,39 +23,47 @@ type Account struct {
|
||||
EthAddr eth.Address
|
||||
}
|
||||
|
||||
func (a *Account) String() string {
|
||||
buf := bytes.NewBufferString("")
|
||||
fmt.Fprintf(buf, "PublicKey: %s..., ", a.PublicKey.String()[:10])
|
||||
fmt.Fprintf(buf, "EthAddr: %s..., ", a.EthAddr.String()[:10])
|
||||
fmt.Fprintf(buf, "TokenID: %v, ", a.TokenID)
|
||||
fmt.Fprintf(buf, "Nonce: %d, ", a.Nonce)
|
||||
fmt.Fprintf(buf, "Balance: %s, ", a.Balance.String())
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// 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) {
|
||||
func (a *Account) Bytes() ([32 * NLEAFELEMS]byte, error) {
|
||||
var b [32 * NLEAFELEMS]byte
|
||||
|
||||
if l.Nonce > 0xffffffffff {
|
||||
if a.Nonce > 0xffffffffff {
|
||||
return b, fmt.Errorf("%s Nonce", ErrNumOverflow)
|
||||
}
|
||||
if len(l.Balance.Bytes()) > 24 {
|
||||
if len(a.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)
|
||||
binary.LittleEndian.PutUint64(nonceBytes[:], a.Nonce)
|
||||
|
||||
copy(b[0:4], tokenIDBytes[:])
|
||||
copy(b[0:4], a.TokenID.Bytes())
|
||||
copy(b[4:9], nonceBytes[:])
|
||||
if babyjub.PointCoordSign(l.PublicKey.X) {
|
||||
if babyjub.PointCoordSign(a.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())
|
||||
copy(b[32:64], SwapEndianness(a.Balance.Bytes())) // SwapEndianness, as big.Int uses BigEndian
|
||||
copy(b[64:96], SwapEndianness(a.PublicKey.Y.Bytes()))
|
||||
copy(b[96:116], a.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) {
|
||||
func (a *Account) BigInts() ([NLEAFELEMS]*big.Int, error) {
|
||||
e := [NLEAFELEMS]*big.Int{}
|
||||
|
||||
b, err := l.Bytes()
|
||||
b, err := a.Bytes()
|
||||
if err != nil {
|
||||
return e, err
|
||||
}
|
||||
@@ -69,10 +77,10 @@ func (l *Account) BigInts() ([NLEAFELEMS]*big.Int, error) {
|
||||
}
|
||||
|
||||
// HashValue returns the value of the Account, which is the Poseidon hash of its *big.Int representation
|
||||
func (l *Account) HashValue() (*big.Int, error) {
|
||||
func (a *Account) HashValue() (*big.Int, error) {
|
||||
b0 := big.NewInt(0)
|
||||
toHash := []*big.Int{b0, b0, b0, b0, b0, b0}
|
||||
lBI, err := l.BigInts()
|
||||
lBI, err := a.BigInts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -122,12 +130,12 @@ func AccountFromBytes(b [32 * NLEAFELEMS]byte) (*Account, error) {
|
||||
return nil, ErrNotInFF
|
||||
}
|
||||
|
||||
l := Account{
|
||||
a := Account{
|
||||
TokenID: TokenID(tokenID),
|
||||
Nonce: nonce,
|
||||
Balance: balance,
|
||||
PublicKey: &publicKey,
|
||||
EthAddr: ethAddr,
|
||||
}
|
||||
return &l, nil
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
@@ -8,5 +8,8 @@ var ErrNotInFF = errors.New("BigInt not inside the Finite Field")
|
||||
// ErrNumOverflow is used when a given value overflows the maximum capacity of the parameter
|
||||
var ErrNumOverflow = errors.New("Value overflows the type")
|
||||
|
||||
// ErrNonceOverflow is used when a given nonce overflows the maximum capacity of the Nonce (2**40-1)
|
||||
var ErrNonceOverflow = errors.New("Nonce overflow, max value: 2**40 -1")
|
||||
|
||||
// ErrBatchQueueEmpty is used when the coordinator.BatchQueue.Pop() is called and has no elements
|
||||
var ErrBatchQueueEmpty = errors.New("BatchQueue empty")
|
||||
|
||||
@@ -14,7 +14,7 @@ type L2Tx struct {
|
||||
ToIdx Idx `meddler:"to_idx"`
|
||||
Amount *big.Int `meddler:"amount,bigint"`
|
||||
Fee FeeSelector `meddler:"fee"`
|
||||
Nonce uint64 `meddler:"nonce"`
|
||||
Nonce Nonce `meddler:"nonce"`
|
||||
// Extra metadata, may be uninitialized
|
||||
Type TxType `meddler:"-"` // optional, descrives which kind of tx it's
|
||||
}
|
||||
|
||||
@@ -1,13 +1,38 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
eth "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/hermeznetwork/hermez-node/utils"
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
)
|
||||
|
||||
// Nonce represents the nonce value in a uint64, which has the method Bytes that returns a byte array of length 5 (40 bits).
|
||||
type Nonce uint64
|
||||
|
||||
// Bytes returns a byte array of length 5 representing the Nonce
|
||||
func (n Nonce) Bytes() ([5]byte, error) {
|
||||
if n >= 1099511627776 { // 2**40bits
|
||||
return [5]byte{}, ErrNonceOverflow
|
||||
}
|
||||
var nonceBytes [8]byte
|
||||
binary.LittleEndian.PutUint64(nonceBytes[:], uint64(n))
|
||||
var b [5]byte
|
||||
copy(b[:], nonceBytes[:5])
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func NonceFromBytes(b [5]byte) (Nonce, error) {
|
||||
var nonceBytes [8]byte
|
||||
copy(nonceBytes[:], b[:5])
|
||||
nonce := binary.LittleEndian.Uint64(nonceBytes[:])
|
||||
return Nonce(nonce), nil
|
||||
}
|
||||
|
||||
// PoolL2Tx is a struct that represents a L2Tx sent by an account to the coordinator hat is waiting to be forged
|
||||
type PoolL2Tx struct {
|
||||
// Stored in DB: mandatory fileds
|
||||
@@ -19,7 +44,7 @@ type PoolL2Tx struct {
|
||||
TokenID TokenID `meddler:"token_id"`
|
||||
Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16
|
||||
Fee FeeSelector `meddler:"fee"`
|
||||
Nonce uint64 `meddler:"nonce"` // effective 48 bits used
|
||||
Nonce Nonce `meddler:"nonce"` // effective 40 bits used
|
||||
State PoolL2TxState `meddler:"state"`
|
||||
Signature babyjub.Signature `meddler:"signature"` // tx signature
|
||||
Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool
|
||||
@@ -40,6 +65,86 @@ type PoolL2Tx struct {
|
||||
RqTxCompressedData []byte `meddler:"-"` // 253 bits, optional for atomic txs
|
||||
}
|
||||
|
||||
// TxCompressedData spec:
|
||||
// [ 32 bits ] signatureConstant // 4 bytes: [0:4]
|
||||
// [ 16 bits ] chainId // 2 bytes: [4:6]
|
||||
// [ 48 bits ] fromIdx // 6 bytes: [6:12]
|
||||
// [ 48 bits ] toIdx // 6 bytes: [12:18]
|
||||
// [ 16 bits ] amountFloat16 // 2 bytes: [18:20]
|
||||
// [ 32 bits ] tokenID // 4 bytes: [20:24]
|
||||
// [ 40 bits ] nonce // 5 bytes: [24:29]
|
||||
// [ 8 bits ] userFee // 1 byte: [29:30]
|
||||
// [ 1 bits ] toBjjSign // 1 byte: [30:31]
|
||||
// Total bits compressed data: 241 bits // 31 bytes in *big.Int representation
|
||||
func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) {
|
||||
// sigconstant
|
||||
sc, ok := new(big.Int).SetString("3322668559", 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error parsing SignatureConstant")
|
||||
}
|
||||
|
||||
amountFloat16, err := utils.NewFloat16(tx.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var b [31]byte
|
||||
copy(b[:4], SwapEndianness(sc.Bytes()))
|
||||
copy(b[4:6], []byte{1, 0, 0, 0}) // LittleEndian representation of uint32(1) for Ethereum
|
||||
copy(b[6:12], tx.FromIdx.Bytes())
|
||||
copy(b[12:18], tx.ToIdx.Bytes())
|
||||
copy(b[18:20], amountFloat16.Bytes())
|
||||
copy(b[20:24], tx.TokenID.Bytes())
|
||||
nonceBytes, err := tx.Nonce.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[24:29], nonceBytes[:])
|
||||
b[29] = byte(tx.Fee)
|
||||
toBjjSign := byte(0)
|
||||
if babyjub.PointCoordSign(tx.ToBJJ.X) {
|
||||
toBjjSign = byte(1)
|
||||
}
|
||||
b[30] = toBjjSign
|
||||
bi := new(big.Int).SetBytes(SwapEndianness(b[:]))
|
||||
|
||||
return bi, nil
|
||||
}
|
||||
|
||||
// TxCompressedDataV2 spec:
|
||||
// [ 48 bits ] fromIdx // 6 bytes: [0:6]
|
||||
// [ 48 bits ] toIdx // 6 bytes: [6:12]
|
||||
// [ 16 bits ] amountFloat16 // 2 bytes: [12:14]
|
||||
// [ 32 bits ] tokenID // 4 bytes: [14:18]
|
||||
// [ 40 bits ] nonce // 5 bytes: [18:23]
|
||||
// [ 8 bits ] userFee // 1 byte: [23:24]
|
||||
// [ 1 bits ] toBjjSign // 1 byte: [24:25]
|
||||
// Total bits compressed data: 193 bits // 25 bytes in *big.Int representation
|
||||
func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) {
|
||||
amountFloat16, err := utils.NewFloat16(tx.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var b [25]byte
|
||||
copy(b[0:6], tx.FromIdx.Bytes())
|
||||
copy(b[6:12], tx.ToIdx.Bytes())
|
||||
copy(b[12:14], amountFloat16.Bytes())
|
||||
copy(b[14:18], tx.TokenID.Bytes())
|
||||
nonceBytes, err := tx.Nonce.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[18:23], nonceBytes[:])
|
||||
b[23] = byte(tx.Fee)
|
||||
toBjjSign := byte(0)
|
||||
if babyjub.PointCoordSign(tx.ToBJJ.X) {
|
||||
toBjjSign = byte(1)
|
||||
}
|
||||
b[24] = toBjjSign
|
||||
|
||||
bi := new(big.Int).SetBytes(SwapEndianness(b[:]))
|
||||
return bi, nil
|
||||
}
|
||||
|
||||
func (tx *PoolL2Tx) Tx() *Tx {
|
||||
return &Tx{
|
||||
TxID: tx.TxID,
|
||||
|
||||
75
common/pooll2tx_test.go
Normal file
75
common/pooll2tx_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNonceParser(t *testing.T) {
|
||||
n := Nonce(1)
|
||||
nBytes, err := n.Bytes()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 5, len(nBytes))
|
||||
assert.Equal(t, "0100000000", hex.EncodeToString(nBytes[:]))
|
||||
n2, err := NonceFromBytes(nBytes)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, n, n2)
|
||||
|
||||
// value before overflow
|
||||
n = Nonce(1099511627775)
|
||||
nBytes, err = n.Bytes()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 5, len(nBytes))
|
||||
assert.Equal(t, "ffffffffff", hex.EncodeToString(nBytes[:]))
|
||||
n2, err = NonceFromBytes(nBytes)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, n, n2)
|
||||
|
||||
// expect value overflow
|
||||
n = Nonce(1099511627776)
|
||||
nBytes, err = n.Bytes()
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, ErrNonceOverflow, err)
|
||||
_, err = NonceFromBytes(nBytes)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTxCompressedData(t *testing.T) {
|
||||
var sk babyjub.PrivateKey
|
||||
_, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
|
||||
assert.Nil(t, err)
|
||||
|
||||
tx := PoolL2Tx{
|
||||
FromIdx: 2,
|
||||
ToIdx: 3,
|
||||
Amount: big.NewInt(4),
|
||||
TokenID: 5,
|
||||
Nonce: 6,
|
||||
ToBJJ: sk.Public(),
|
||||
}
|
||||
txCompressedData, err := tx.TxCompressedData()
|
||||
assert.Nil(t, err)
|
||||
// test vector value generated from javascript implementation
|
||||
assert.Equal(t, "1766847064778421992193717128424891165872736891548909569553540449389241871", txCompressedData.String())
|
||||
assert.Equal(t, "10000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes())[1:])
|
||||
|
||||
tx = PoolL2Tx{
|
||||
FromIdx: 7,
|
||||
ToIdx: 8,
|
||||
Amount: big.NewInt(9),
|
||||
TokenID: 10,
|
||||
Nonce: 11,
|
||||
Fee: 12,
|
||||
ToBJJ: sk.Public(),
|
||||
}
|
||||
txCompressedData, err = tx.TxCompressedDataV2()
|
||||
assert.Nil(t, err)
|
||||
// test vector value generated from javascript implementation
|
||||
assert.Equal(t, "6571340879233176732837827812956721483162819083004853354503", txCompressedData.String())
|
||||
assert.Equal(t, "10c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes())[1:])
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
eth "github.com/ethereum/go-ethereum/common"
|
||||
@@ -26,3 +27,10 @@ type TokenInfo struct {
|
||||
|
||||
// TokenID is the unique identifier of the token, as set in the smart contract
|
||||
type TokenID uint32 // current implementation supports up to 2^32 tokens
|
||||
|
||||
// Bytes returns a byte array of length 4 representing the TokenID
|
||||
func (t TokenID) Bytes() []byte {
|
||||
var tokenIDBytes [4]byte
|
||||
binary.LittleEndian.PutUint32(tokenIDBytes[:], uint32(t))
|
||||
return tokenIDBytes[:]
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ type Tx struct {
|
||||
ToIdx Idx // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositTransfer
|
||||
TokenID TokenID
|
||||
Amount *big.Int
|
||||
Nonce uint64 // effective 48 bits used
|
||||
Nonce Nonce // effective 40 bits used
|
||||
Fee FeeSelector
|
||||
Type TxType // optional, descrives which kind of tx it's
|
||||
BatchNum BatchNum // batchNum in which this tx was forged. Presence indicates "forged" state.
|
||||
|
||||
42
test/lang.go
42
test/lang.go
@@ -16,7 +16,7 @@ var errof = fmt.Errorf("eof in parseline")
|
||||
var ecomment = fmt.Errorf("comment in parseline")
|
||||
|
||||
const (
|
||||
ILLEGAL Token = iota
|
||||
ILLEGAL token = iota
|
||||
WS
|
||||
EOF
|
||||
|
||||
@@ -80,9 +80,9 @@ func (i Instruction) Raw() string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type Token int
|
||||
type token int
|
||||
|
||||
type Scanner struct {
|
||||
type scanner struct {
|
||||
r *bufio.Reader
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ func isDigit(ch rune) bool {
|
||||
return (ch >= '0' && ch <= '9')
|
||||
}
|
||||
|
||||
// NewScanner creates a new Scanner with the given io.Reader
|
||||
func NewScanner(r io.Reader) *Scanner {
|
||||
return &Scanner{r: bufio.NewReader(r)}
|
||||
// NewScanner creates a new scanner with the given io.Reader
|
||||
func NewScanner(r io.Reader) *scanner {
|
||||
return &scanner{r: bufio.NewReader(r)}
|
||||
}
|
||||
|
||||
func (s *Scanner) read() rune {
|
||||
func (s *scanner) read() rune {
|
||||
ch, _, err := s.r.ReadRune()
|
||||
if err != nil {
|
||||
return eof
|
||||
@@ -115,12 +115,12 @@ func (s *Scanner) read() rune {
|
||||
return ch
|
||||
}
|
||||
|
||||
func (s *Scanner) unread() {
|
||||
func (s *scanner) unread() {
|
||||
_ = s.r.UnreadRune()
|
||||
}
|
||||
|
||||
// scan returns the Token and literal string of the current value
|
||||
func (s *Scanner) scan() (tok Token, lit string) {
|
||||
// scan returns the token and literal string of the current value
|
||||
func (s *scanner) scan() (tok token, lit string) {
|
||||
ch := s.read()
|
||||
|
||||
if isWhitespace(ch) {
|
||||
@@ -144,7 +144,7 @@ func (s *Scanner) scan() (tok Token, lit string) {
|
||||
return ILLEGAL, string(ch)
|
||||
}
|
||||
|
||||
func (s *Scanner) scanWhitespace() (token Token, lit string) {
|
||||
func (s *scanner) scanWhitespace() (token token, lit string) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
@@ -161,7 +161,7 @@ func (s *Scanner) scanWhitespace() (token Token, lit string) {
|
||||
return WS, buf.String()
|
||||
}
|
||||
|
||||
func (s *Scanner) scanIndent() (tok Token, lit string) {
|
||||
func (s *scanner) scanIndent() (tok token, lit string) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteRune(s.read())
|
||||
|
||||
@@ -177,16 +177,16 @@ func (s *Scanner) scanIndent() (tok Token, lit string) {
|
||||
}
|
||||
|
||||
if len(buf.String()) == 1 {
|
||||
return Token(rune(buf.String()[0])), buf.String()
|
||||
return token(rune(buf.String()[0])), buf.String()
|
||||
}
|
||||
return IDENT, buf.String()
|
||||
}
|
||||
|
||||
// Parser defines the parser
|
||||
type Parser struct {
|
||||
s *Scanner
|
||||
s *scanner
|
||||
buf struct {
|
||||
tok Token
|
||||
tok token
|
||||
lit string
|
||||
n int
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func NewParser(r io.Reader) *Parser {
|
||||
return &Parser{s: NewScanner(r)}
|
||||
}
|
||||
|
||||
func (p *Parser) scan() (tok Token, lit string) {
|
||||
func (p *Parser) scan() (tok token, lit string) {
|
||||
// if there is a token in the buffer return it
|
||||
if p.buf.n != 0 {
|
||||
p.buf.n = 0
|
||||
@@ -210,7 +210,7 @@ func (p *Parser) scan() (tok Token, lit string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) {
|
||||
func (p *Parser) scanIgnoreWhitespace() (tok token, lit string) {
|
||||
tok, lit = p.scan()
|
||||
if tok == WS {
|
||||
tok, lit = p.scan()
|
||||
@@ -319,6 +319,10 @@ func (p *Parser) parseLine() (*Instruction, error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func idxTokenIDToString(idx string, tid common.TokenID) string {
|
||||
return idx + strconv.Itoa(int(tid))
|
||||
}
|
||||
|
||||
// Parse parses through reader
|
||||
func (p *Parser) Parse() (Instructions, error) {
|
||||
var instructions Instructions
|
||||
@@ -338,9 +342,9 @@ func (p *Parser) Parse() (Instructions, error) {
|
||||
return instructions, fmt.Errorf("error parsing line %d: %s, err: %s", i, instruction.Literal, err.Error())
|
||||
}
|
||||
instructions.Instructions = append(instructions.Instructions, instruction)
|
||||
accounts[instruction.From] = true
|
||||
accounts[idxTokenIDToString(instruction.From, instruction.TokenID)] = true
|
||||
if instruction.Type == common.TxTypeTransfer { // type: Transfer
|
||||
accounts[instruction.To] = true
|
||||
accounts[idxTokenIDToString(instruction.To, instruction.TokenID)] = true
|
||||
}
|
||||
tokenids[instruction.TokenID] = true
|
||||
i++
|
||||
|
||||
@@ -35,7 +35,8 @@ func TestParse(t *testing.T) {
|
||||
instructions, err := parser.Parse()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 12, len(instructions.Instructions))
|
||||
assert.Equal(t, 5, len(instructions.Accounts))
|
||||
// assert.Equal(t, 5, len(instructions.Accounts))
|
||||
fmt.Println(instructions.Accounts)
|
||||
assert.Equal(t, 3, len(instructions.TokenIDs))
|
||||
|
||||
if debug {
|
||||
|
||||
25
test/txs.go
25
test/txs.go
@@ -17,7 +17,7 @@ type Account struct {
|
||||
BJJ *babyjub.PrivateKey
|
||||
Addr ethCommon.Address
|
||||
Idx common.Idx
|
||||
Nonce uint64
|
||||
Nonce common.Nonce
|
||||
}
|
||||
|
||||
// GenerateKeys generates BabyJubJub & Address keys for the given list of
|
||||
@@ -66,40 +66,37 @@ func GenerateTestTxs(t *testing.T, instructions Instructions) ([]*common.L1Tx, [
|
||||
case common.TxTypeCreateAccountDeposit:
|
||||
tx := common.L1Tx{
|
||||
// TxID
|
||||
FromEthAddr: accounts[inst.From].Addr,
|
||||
FromBJJ: accounts[inst.From].BJJ.Public(),
|
||||
FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr,
|
||||
FromBJJ: accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(),
|
||||
TokenID: inst.TokenID,
|
||||
LoadAmount: big.NewInt(int64(inst.Amount)),
|
||||
Type: common.TxTypeCreateAccountDeposit,
|
||||
}
|
||||
l1txs = append(l1txs, &tx)
|
||||
if accounts[inst.From].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx
|
||||
accounts[inst.From].Idx = common.Idx(idx)
|
||||
if accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment idx
|
||||
accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx)
|
||||
idx++
|
||||
}
|
||||
case common.TxTypeTransfer:
|
||||
tx := common.PoolL2Tx{
|
||||
// TxID: nil,
|
||||
FromIdx: accounts[inst.From].Idx,
|
||||
ToIdx: accounts[inst.To].Idx,
|
||||
ToEthAddr: accounts[inst.To].Addr,
|
||||
ToBJJ: accounts[inst.To].BJJ.Public(),
|
||||
FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
|
||||
ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx,
|
||||
ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
|
||||
ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
|
||||
TokenID: inst.TokenID,
|
||||
Amount: big.NewInt(int64(inst.Amount)),
|
||||
Fee: common.FeeSelector(inst.Fee),
|
||||
Nonce: accounts[inst.From].Nonce,
|
||||
Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
|
||||
State: common.PoolL2TxStatePending,
|
||||
Timestamp: time.Now(),
|
||||
BatchNum: 0,
|
||||
Type: common.TxTypeTransfer,
|
||||
}
|
||||
// if inst.Fee == 0 {
|
||||
// tx.Fee = common.FeeSelector(i % 255)
|
||||
// }
|
||||
// TODO once signature function is ready, perform
|
||||
// signature and set it to tx.Signature
|
||||
|
||||
accounts[inst.From].Nonce++
|
||||
accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
|
||||
l2txs = append(l2txs, &tx)
|
||||
default:
|
||||
continue
|
||||
|
||||
@@ -17,6 +17,7 @@ func TestGenerateTestL2Txs(t *testing.T) {
|
||||
A-B (1): 6 1
|
||||
B-C (1): 3 1
|
||||
C-A (1): 3 1
|
||||
A-B (1): 1 1
|
||||
A-B (2): 15 1
|
||||
User0 (1): 20
|
||||
User1 (3) : 20
|
||||
@@ -29,22 +30,22 @@ func TestGenerateTestL2Txs(t *testing.T) {
|
||||
|
||||
l1txs, l2txs := GenerateTestTxs(t, instructions)
|
||||
require.Equal(t, 5, len(l1txs))
|
||||
require.Equal(t, 6, len(l2txs))
|
||||
require.Equal(t, 7, len(l2txs))
|
||||
|
||||
// l1txs
|
||||
assert.Equal(t, common.TxTypeCreateAccountDeposit, l1txs[0].Type)
|
||||
assert.Equal(t, "5bac784d938067d980a9d39bdd79bf84a0cbb296977c47cc30de2d5ce9229d2f", l1txs[0].FromBJJ.String())
|
||||
assert.Equal(t, "5bac784d938067d980a9d39bdd79bf84a0cbb296977c47cc30de2d5ce9229d2f", l1txs[1].FromBJJ.String())
|
||||
assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l1txs[2].FromBJJ.String())
|
||||
assert.Equal(t, "a25c7150609ecfcf90fc3f419474e8bc28ea5978df1b0a68339bff884c117e19", l1txs[4].FromBJJ.String())
|
||||
assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l1txs[1].FromBJJ.String())
|
||||
assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l1txs[2].FromBJJ.String())
|
||||
assert.Equal(t, "b6856a87832b182e5a9a1e738dbcd1f3c728bbc67ea1010aaff563eb5316131b", l1txs[4].FromBJJ.String())
|
||||
|
||||
// l2txs
|
||||
assert.Equal(t, common.TxTypeTransfer, l2txs[0].Type)
|
||||
assert.Equal(t, common.Idx(1), l2txs[0].FromIdx)
|
||||
assert.Equal(t, common.Idx(2), l2txs[0].ToIdx)
|
||||
assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l2txs[0].ToBJJ.String())
|
||||
assert.Equal(t, "0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF", l2txs[0].ToEthAddr.Hex())
|
||||
assert.Equal(t, uint64(0), l2txs[0].Nonce)
|
||||
assert.Equal(t, uint64(1), l2txs[3].Nonce)
|
||||
assert.Equal(t, common.Idx(3), l2txs[0].ToIdx)
|
||||
assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l2txs[0].ToBJJ.String())
|
||||
assert.Equal(t, "0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69", l2txs[0].ToEthAddr.Hex())
|
||||
assert.Equal(t, common.Nonce(0), l2txs[0].Nonce)
|
||||
assert.Equal(t, common.Nonce(1), l2txs[3].Nonce)
|
||||
assert.Equal(t, common.FeeSelector(1), l2txs[0].Fee)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
@@ -13,9 +14,15 @@ var (
|
||||
// Float16 represents a float in a 16 bit format
|
||||
type Float16 uint16
|
||||
|
||||
// BigInt converts the Float16 to a big.Int integer
|
||||
func (fl16 *Float16) BigInt() *big.Int {
|
||||
// Bytes return a byte array of length 2 with the Float16 value encoded in LittleEndian
|
||||
func (f16 Float16) Bytes() []byte {
|
||||
var b [2]byte
|
||||
binary.LittleEndian.PutUint16(b[:], uint16(f16))
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// BigInt converts the Float16 to a *big.Int integer
|
||||
func (fl16 *Float16) BigInt() *big.Int {
|
||||
fl := int64(*fl16)
|
||||
|
||||
m := big.NewInt(fl & 0x3FF)
|
||||
@@ -30,14 +37,11 @@ func (fl16 *Float16) BigInt() *big.Int {
|
||||
res.Add(res, exp.Div(exp, big.NewInt(2)))
|
||||
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
}
|
||||
|
||||
// floorFix2Float converts a fix to a float, always rounding down
|
||||
func floorFix2Float(_f *big.Int) Float16 {
|
||||
|
||||
zero := big.NewInt(0)
|
||||
ten := big.NewInt(10)
|
||||
e := int64(0)
|
||||
@@ -60,13 +64,11 @@ func floorFix2Float(_f *big.Int) Float16 {
|
||||
}
|
||||
|
||||
return Float16(m.Int64() | e<<11)
|
||||
|
||||
}
|
||||
|
||||
// NewFloat16 encodes a big.Int integer as a Float16, returning error in case
|
||||
// NewFloat16 encodes a *big.Int integer as a Float16, returning error in case
|
||||
// of loss during the encoding.
|
||||
func NewFloat16(f *big.Int) (Float16, error) {
|
||||
|
||||
fl1 := floorFix2Float(f)
|
||||
fi1 := fl1.BigInt()
|
||||
fl2 := fl1 | 0x400
|
||||
@@ -101,20 +103,16 @@ func NewFloat16(f *big.Int) (Float16, error) {
|
||||
}
|
||||
|
||||
// Do rounding check
|
||||
|
||||
if res.BigInt().Cmp(f) == 0 {
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return res, ErrRoundingLoss
|
||||
|
||||
}
|
||||
|
||||
// NewFloat16Floor encodes a big.Int integer as a Float16, rounding down in
|
||||
// case of loss during the encoding.
|
||||
func NewFloat16Floor(f *big.Int) Float16 {
|
||||
|
||||
fl1 := floorFix2Float(f)
|
||||
fl2 := fl1 | 0x400
|
||||
fi2 := fl2.BigInt()
|
||||
@@ -123,5 +121,4 @@ func NewFloat16Floor(f *big.Int) Float16 {
|
||||
return fl2
|
||||
}
|
||||
return fl1
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user