From a28c81f4aedef88ddcc308099966a4c4c4a517b1 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Fri, 21 Aug 2020 15:01:01 +0200 Subject: [PATCH] Add TxCompressedData & V2 with some extra parsers for common --- common/account.go | 42 +++++++++------- common/errors.go | 3 ++ common/l2tx.go | 2 +- common/pooll2tx.go | 107 +++++++++++++++++++++++++++++++++++++++- common/pooll2tx_test.go | 75 ++++++++++++++++++++++++++++ common/token.go | 8 +++ common/tx.go | 2 +- test/lang.go | 42 +++++++++------- test/lang_test.go | 3 +- test/txs.go | 25 +++++----- test/txs_test.go | 19 +++---- utils/utils.go | 23 ++++----- 12 files changed, 275 insertions(+), 76 deletions(-) create mode 100644 common/pooll2tx_test.go diff --git a/common/account.go b/common/account.go index b6cc425..931d453 100644 --- a/common/account.go +++ b/common/account.go @@ -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 } diff --git a/common/errors.go b/common/errors.go index a3609c2..4fc0a6a 100644 --- a/common/errors.go +++ b/common/errors.go @@ -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") diff --git a/common/l2tx.go b/common/l2tx.go index 47ad093..b261f6e 100644 --- a/common/l2tx.go +++ b/common/l2tx.go @@ -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 } diff --git a/common/pooll2tx.go b/common/pooll2tx.go index 0519a65..721691c 100644 --- a/common/pooll2tx.go +++ b/common/pooll2tx.go @@ -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, diff --git a/common/pooll2tx_test.go b/common/pooll2tx_test.go new file mode 100644 index 0000000..d555539 --- /dev/null +++ b/common/pooll2tx_test.go @@ -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:]) + +} diff --git a/common/token.go b/common/token.go index 62f0126..0a38a01 100644 --- a/common/token.go +++ b/common/token.go @@ -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[:] +} diff --git a/common/tx.go b/common/tx.go index 368e0e6..dcef756 100644 --- a/common/tx.go +++ b/common/tx.go @@ -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. diff --git a/test/lang.go b/test/lang.go index 51f4348..33de602 100644 --- a/test/lang.go +++ b/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++ diff --git a/test/lang_test.go b/test/lang_test.go index deb4522..7a0a65b 100644 --- a/test/lang_test.go +++ b/test/lang_test.go @@ -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 { diff --git a/test/txs.go b/test/txs.go index 17d151c..bbdb9b3 100644 --- a/test/txs.go +++ b/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 diff --git a/test/txs_test.go b/test/txs_test.go index 9a3d2b0..91e6310 100644 --- a/test/txs_test.go +++ b/test/txs_test.go @@ -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) } diff --git a/utils/utils.go b/utils/utils.go index 4076eeb..104a073 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -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 - }