arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
Add TxCompressedData & V2 with some extra parsers for common
arnaucube root@arnaucube.com
Fri, 21 Aug 2020 15:01:01 +0200
commit

a28c81f4aedef88ddcc308099966a4c4c4a517b1

parent

3f63cd7cd02823b9b51c310f60a82c76b81e4bdb

M common/account.go → common/account.go

@@ -23,39 +23,47 @@ PublicKey *babyjub.PublicKey

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 @@ 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) { +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 @@ if !cryptoUtils.CheckBigIntInField(ay) {

return nil, ErrNotInFF } - l := Account{ + a := Account{ TokenID: TokenID(tokenID), Nonce: nonce, Balance: balance, PublicKey: &publicKey, EthAddr: ethAddr, } - return &l, nil + return &a, nil }
M common/errors.go → common/errors.go

@@ -8,5 +8,8 @@

// 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")
M common/l2tx.go → common/l2tx.go

@@ -14,7 +14,7 @@ FromIdx Idx `meddler:"from_idx"`

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 }
M common/pooll2tx.go → 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 @@ ToBJJ *babyjub.PublicKey `meddler:"to_bjj"` // TODO: stop using json, use scanner/valuer

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

@@ -38,6 +63,86 @@ AbsoluteFeeUpdate time.Time `meddler:"absolute_fee_update,utctimez"`

// Extra metadata, may be uninitialized Type TxType `meddler:"-"` // optional, descrives which kind of tx it's 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 {
A 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:]) + +}
M common/token.go → common/token.go

@@ -1,6 +1,7 @@

package common import ( + "encoding/binary" "time" eth "github.com/ethereum/go-ethereum/common"

@@ -26,3 +27,10 @@ }

// 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[:] +}
M common/tx.go → common/tx.go

@@ -66,7 +66,7 @@ FromIdx Idx // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit)

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.
M test/lang.go → 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 @@ }

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 @@ }

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 @@

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 @@ }

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 @@ }

} 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 @@

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 @@ }

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 @@ if err != nil {

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++
M test/lang_test.go → test/lang_test.go

@@ -35,7 +35,8 @@ parser := NewParser(strings.NewReader(s))

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 {
M test/txs.go → 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 @@ switch inst.Type {

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
M test/txs_test.go → test/txs_test.go

@@ -17,6 +17,7 @@ B (1): 5

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 @@ assert.Nil(t, err)

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) }
M utils/utils.go → utils/utils.go

@@ -1,6 +1,7 @@

package utils import ( + "encoding/binary" "errors" "math/big" )

@@ -13,9 +14,15 @@

// 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 @@

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 @@

} 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 @@ res = fl3

} // 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 @@ if fi2.Cmp(f) < 1 {

return fl2 } return fl1 - }