mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 03:16:45 +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
|
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.
|
// 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
|
var b [32 * NLEAFELEMS]byte
|
||||||
|
|
||||||
if l.Nonce > 0xffffffffff {
|
if a.Nonce > 0xffffffffff {
|
||||||
return b, fmt.Errorf("%s Nonce", ErrNumOverflow)
|
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)
|
return b, fmt.Errorf("%s Balance", ErrNumOverflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokenIDBytes [4]byte
|
|
||||||
binary.LittleEndian.PutUint32(tokenIDBytes[:], uint32(l.TokenID))
|
|
||||||
var nonceBytes [8]byte
|
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[:])
|
copy(b[4:9], nonceBytes[:])
|
||||||
if babyjub.PointCoordSign(l.PublicKey.X) {
|
if babyjub.PointCoordSign(a.PublicKey.X) {
|
||||||
b[10] = 1
|
b[10] = 1
|
||||||
}
|
}
|
||||||
copy(b[32:64], SwapEndianness(l.Balance.Bytes())) // SwapEndianness, as big.Int uses BigEndian
|
copy(b[32:64], SwapEndianness(a.Balance.Bytes())) // SwapEndianness, as big.Int uses BigEndian
|
||||||
copy(b[64:96], SwapEndianness(l.PublicKey.Y.Bytes()))
|
copy(b[64:96], SwapEndianness(a.PublicKey.Y.Bytes()))
|
||||||
copy(b[96:116], l.EthAddr.Bytes())
|
copy(b[96:116], a.EthAddr.Bytes())
|
||||||
|
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BigInts returns the [5]*big.Int, where each *big.Int is inside the Finite Field
|
// 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{}
|
e := [NLEAFELEMS]*big.Int{}
|
||||||
|
|
||||||
b, err := l.Bytes()
|
b, err := a.Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return e, err
|
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
|
// 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)
|
b0 := big.NewInt(0)
|
||||||
toHash := []*big.Int{b0, b0, b0, b0, b0, b0}
|
toHash := []*big.Int{b0, b0, b0, b0, b0, b0}
|
||||||
lBI, err := l.BigInts()
|
lBI, err := a.BigInts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -122,12 +130,12 @@ func AccountFromBytes(b [32 * NLEAFELEMS]byte) (*Account, error) {
|
|||||||
return nil, ErrNotInFF
|
return nil, ErrNotInFF
|
||||||
}
|
}
|
||||||
|
|
||||||
l := Account{
|
a := Account{
|
||||||
TokenID: TokenID(tokenID),
|
TokenID: TokenID(tokenID),
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
Balance: balance,
|
Balance: balance,
|
||||||
PublicKey: &publicKey,
|
PublicKey: &publicKey,
|
||||||
EthAddr: ethAddr,
|
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
|
// ErrNumOverflow is used when a given value overflows the maximum capacity of the parameter
|
||||||
var ErrNumOverflow = errors.New("Value overflows the type")
|
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
|
// ErrBatchQueueEmpty is used when the coordinator.BatchQueue.Pop() is called and has no elements
|
||||||
var ErrBatchQueueEmpty = errors.New("BatchQueue empty")
|
var ErrBatchQueueEmpty = errors.New("BatchQueue empty")
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type L2Tx struct {
|
|||||||
ToIdx Idx `meddler:"to_idx"`
|
ToIdx Idx `meddler:"to_idx"`
|
||||||
Amount *big.Int `meddler:"amount,bigint"`
|
Amount *big.Int `meddler:"amount,bigint"`
|
||||||
Fee FeeSelector `meddler:"fee"`
|
Fee FeeSelector `meddler:"fee"`
|
||||||
Nonce uint64 `meddler:"nonce"`
|
Nonce Nonce `meddler:"nonce"`
|
||||||
// Extra metadata, may be uninitialized
|
// Extra metadata, may be uninitialized
|
||||||
Type TxType `meddler:"-"` // optional, descrives which kind of tx it's
|
Type TxType `meddler:"-"` // optional, descrives which kind of tx it's
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,38 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth "github.com/ethereum/go-ethereum/common"
|
eth "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/hermeznetwork/hermez-node/utils"
|
||||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
"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
|
// PoolL2Tx is a struct that represents a L2Tx sent by an account to the coordinator hat is waiting to be forged
|
||||||
type PoolL2Tx struct {
|
type PoolL2Tx struct {
|
||||||
// Stored in DB: mandatory fileds
|
// Stored in DB: mandatory fileds
|
||||||
@@ -19,7 +44,7 @@ type PoolL2Tx struct {
|
|||||||
TokenID TokenID `meddler:"token_id"`
|
TokenID TokenID `meddler:"token_id"`
|
||||||
Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16
|
Amount *big.Int `meddler:"amount,bigint"` // TODO: change to float16
|
||||||
Fee FeeSelector `meddler:"fee"`
|
Fee FeeSelector `meddler:"fee"`
|
||||||
Nonce uint64 `meddler:"nonce"` // effective 48 bits used
|
Nonce Nonce `meddler:"nonce"` // effective 40 bits used
|
||||||
State PoolL2TxState `meddler:"state"`
|
State PoolL2TxState `meddler:"state"`
|
||||||
Signature babyjub.Signature `meddler:"signature"` // tx signature
|
Signature babyjub.Signature `meddler:"signature"` // tx signature
|
||||||
Timestamp time.Time `meddler:"timestamp,utctime"` // time when added to the tx pool
|
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
|
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 {
|
func (tx *PoolL2Tx) Tx() *Tx {
|
||||||
return &Tx{
|
return &Tx{
|
||||||
TxID: tx.TxID,
|
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
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
eth "github.com/ethereum/go-ethereum/common"
|
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
|
// 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
|
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
|
ToIdx Idx // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositTransfer
|
||||||
TokenID TokenID
|
TokenID TokenID
|
||||||
Amount *big.Int
|
Amount *big.Int
|
||||||
Nonce uint64 // effective 48 bits used
|
Nonce Nonce // effective 40 bits used
|
||||||
Fee FeeSelector
|
Fee FeeSelector
|
||||||
Type TxType // optional, descrives which kind of tx it's
|
Type TxType // optional, descrives which kind of tx it's
|
||||||
BatchNum BatchNum // batchNum in which this tx was forged. Presence indicates "forged" state.
|
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")
|
var ecomment = fmt.Errorf("comment in parseline")
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ILLEGAL Token = iota
|
ILLEGAL token = iota
|
||||||
WS
|
WS
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -80,9 +80,9 @@ func (i Instruction) Raw() string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type Token int
|
type token int
|
||||||
|
|
||||||
type Scanner struct {
|
type scanner struct {
|
||||||
r *bufio.Reader
|
r *bufio.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,12 +102,12 @@ func isDigit(ch rune) bool {
|
|||||||
return (ch >= '0' && ch <= '9')
|
return (ch >= '0' && ch <= '9')
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewScanner creates a new Scanner with the given io.Reader
|
// NewScanner creates a new scanner with the given io.Reader
|
||||||
func NewScanner(r io.Reader) *Scanner {
|
func NewScanner(r io.Reader) *scanner {
|
||||||
return &Scanner{r: bufio.NewReader(r)}
|
return &scanner{r: bufio.NewReader(r)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) read() rune {
|
func (s *scanner) read() rune {
|
||||||
ch, _, err := s.r.ReadRune()
|
ch, _, err := s.r.ReadRune()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return eof
|
return eof
|
||||||
@@ -115,12 +115,12 @@ func (s *Scanner) read() rune {
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) unread() {
|
func (s *scanner) unread() {
|
||||||
_ = s.r.UnreadRune()
|
_ = s.r.UnreadRune()
|
||||||
}
|
}
|
||||||
|
|
||||||
// scan returns the Token and literal string of the current value
|
// scan returns the token and literal string of the current value
|
||||||
func (s *Scanner) scan() (tok Token, lit string) {
|
func (s *scanner) scan() (tok token, lit string) {
|
||||||
ch := s.read()
|
ch := s.read()
|
||||||
|
|
||||||
if isWhitespace(ch) {
|
if isWhitespace(ch) {
|
||||||
@@ -144,7 +144,7 @@ func (s *Scanner) scan() (tok Token, lit string) {
|
|||||||
return ILLEGAL, string(ch)
|
return ILLEGAL, string(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Scanner) scanWhitespace() (token Token, lit string) {
|
func (s *scanner) scanWhitespace() (token token, lit string) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteRune(s.read())
|
buf.WriteRune(s.read())
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ func (s *Scanner) scanWhitespace() (token Token, lit string) {
|
|||||||
return WS, buf.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
|
var buf bytes.Buffer
|
||||||
buf.WriteRune(s.read())
|
buf.WriteRune(s.read())
|
||||||
|
|
||||||
@@ -177,16 +177,16 @@ func (s *Scanner) scanIndent() (tok Token, lit string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(buf.String()) == 1 {
|
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()
|
return IDENT, buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser defines the parser
|
// Parser defines the parser
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
s *Scanner
|
s *scanner
|
||||||
buf struct {
|
buf struct {
|
||||||
tok Token
|
tok token
|
||||||
lit string
|
lit string
|
||||||
n int
|
n int
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ func NewParser(r io.Reader) *Parser {
|
|||||||
return &Parser{s: NewScanner(r)}
|
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 there is a token in the buffer return it
|
||||||
if p.buf.n != 0 {
|
if p.buf.n != 0 {
|
||||||
p.buf.n = 0
|
p.buf.n = 0
|
||||||
@@ -210,7 +210,7 @@ func (p *Parser) scan() (tok Token, lit string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) {
|
func (p *Parser) scanIgnoreWhitespace() (tok token, lit string) {
|
||||||
tok, lit = p.scan()
|
tok, lit = p.scan()
|
||||||
if tok == WS {
|
if tok == WS {
|
||||||
tok, lit = p.scan()
|
tok, lit = p.scan()
|
||||||
@@ -319,6 +319,10 @@ func (p *Parser) parseLine() (*Instruction, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func idxTokenIDToString(idx string, tid common.TokenID) string {
|
||||||
|
return idx + strconv.Itoa(int(tid))
|
||||||
|
}
|
||||||
|
|
||||||
// Parse parses through reader
|
// Parse parses through reader
|
||||||
func (p *Parser) Parse() (Instructions, error) {
|
func (p *Parser) Parse() (Instructions, error) {
|
||||||
var instructions Instructions
|
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())
|
return instructions, fmt.Errorf("error parsing line %d: %s, err: %s", i, instruction.Literal, err.Error())
|
||||||
}
|
}
|
||||||
instructions.Instructions = append(instructions.Instructions, instruction)
|
instructions.Instructions = append(instructions.Instructions, instruction)
|
||||||
accounts[instruction.From] = true
|
accounts[idxTokenIDToString(instruction.From, instruction.TokenID)] = true
|
||||||
if instruction.Type == common.TxTypeTransfer { // type: Transfer
|
if instruction.Type == common.TxTypeTransfer { // type: Transfer
|
||||||
accounts[instruction.To] = true
|
accounts[idxTokenIDToString(instruction.To, instruction.TokenID)] = true
|
||||||
}
|
}
|
||||||
tokenids[instruction.TokenID] = true
|
tokenids[instruction.TokenID] = true
|
||||||
i++
|
i++
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ func TestParse(t *testing.T) {
|
|||||||
instructions, err := parser.Parse()
|
instructions, err := parser.Parse()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 12, len(instructions.Instructions))
|
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))
|
assert.Equal(t, 3, len(instructions.TokenIDs))
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
|
|||||||
25
test/txs.go
25
test/txs.go
@@ -17,7 +17,7 @@ type Account struct {
|
|||||||
BJJ *babyjub.PrivateKey
|
BJJ *babyjub.PrivateKey
|
||||||
Addr ethCommon.Address
|
Addr ethCommon.Address
|
||||||
Idx common.Idx
|
Idx common.Idx
|
||||||
Nonce uint64
|
Nonce common.Nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateKeys generates BabyJubJub & Address keys for the given list of
|
// 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:
|
case common.TxTypeCreateAccountDeposit:
|
||||||
tx := common.L1Tx{
|
tx := common.L1Tx{
|
||||||
// TxID
|
// TxID
|
||||||
FromEthAddr: accounts[inst.From].Addr,
|
FromEthAddr: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Addr,
|
||||||
FromBJJ: accounts[inst.From].BJJ.Public(),
|
FromBJJ: accounts[idxTokenIDToString(inst.From, inst.TokenID)].BJJ.Public(),
|
||||||
TokenID: inst.TokenID,
|
TokenID: inst.TokenID,
|
||||||
LoadAmount: big.NewInt(int64(inst.Amount)),
|
LoadAmount: big.NewInt(int64(inst.Amount)),
|
||||||
Type: common.TxTypeCreateAccountDeposit,
|
Type: common.TxTypeCreateAccountDeposit,
|
||||||
}
|
}
|
||||||
l1txs = append(l1txs, &tx)
|
l1txs = append(l1txs, &tx)
|
||||||
if accounts[inst.From].Idx == common.Idx(0) { // if account.Idx is not set yet, set it and increment 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[inst.From].Idx = common.Idx(idx)
|
accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx = common.Idx(idx)
|
||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
case common.TxTypeTransfer:
|
case common.TxTypeTransfer:
|
||||||
tx := common.PoolL2Tx{
|
tx := common.PoolL2Tx{
|
||||||
// TxID: nil,
|
// TxID: nil,
|
||||||
FromIdx: accounts[inst.From].Idx,
|
FromIdx: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Idx,
|
||||||
ToIdx: accounts[inst.To].Idx,
|
ToIdx: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Idx,
|
||||||
ToEthAddr: accounts[inst.To].Addr,
|
ToEthAddr: accounts[idxTokenIDToString(inst.To, inst.TokenID)].Addr,
|
||||||
ToBJJ: accounts[inst.To].BJJ.Public(),
|
ToBJJ: accounts[idxTokenIDToString(inst.To, inst.TokenID)].BJJ.Public(),
|
||||||
TokenID: inst.TokenID,
|
TokenID: inst.TokenID,
|
||||||
Amount: big.NewInt(int64(inst.Amount)),
|
Amount: big.NewInt(int64(inst.Amount)),
|
||||||
Fee: common.FeeSelector(inst.Fee),
|
Fee: common.FeeSelector(inst.Fee),
|
||||||
Nonce: accounts[inst.From].Nonce,
|
Nonce: accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce,
|
||||||
State: common.PoolL2TxStatePending,
|
State: common.PoolL2TxStatePending,
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
BatchNum: 0,
|
BatchNum: 0,
|
||||||
Type: common.TxTypeTransfer,
|
Type: common.TxTypeTransfer,
|
||||||
}
|
}
|
||||||
// if inst.Fee == 0 {
|
|
||||||
// tx.Fee = common.FeeSelector(i % 255)
|
|
||||||
// }
|
|
||||||
// TODO once signature function is ready, perform
|
// TODO once signature function is ready, perform
|
||||||
// signature and set it to tx.Signature
|
// signature and set it to tx.Signature
|
||||||
|
|
||||||
accounts[inst.From].Nonce++
|
accounts[idxTokenIDToString(inst.From, inst.TokenID)].Nonce++
|
||||||
l2txs = append(l2txs, &tx)
|
l2txs = append(l2txs, &tx)
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ func TestGenerateTestL2Txs(t *testing.T) {
|
|||||||
A-B (1): 6 1
|
A-B (1): 6 1
|
||||||
B-C (1): 3 1
|
B-C (1): 3 1
|
||||||
C-A (1): 3 1
|
C-A (1): 3 1
|
||||||
|
A-B (1): 1 1
|
||||||
A-B (2): 15 1
|
A-B (2): 15 1
|
||||||
User0 (1): 20
|
User0 (1): 20
|
||||||
User1 (3) : 20
|
User1 (3) : 20
|
||||||
@@ -29,22 +30,22 @@ func TestGenerateTestL2Txs(t *testing.T) {
|
|||||||
|
|
||||||
l1txs, l2txs := GenerateTestTxs(t, instructions)
|
l1txs, l2txs := GenerateTestTxs(t, instructions)
|
||||||
require.Equal(t, 5, len(l1txs))
|
require.Equal(t, 5, len(l1txs))
|
||||||
require.Equal(t, 6, len(l2txs))
|
require.Equal(t, 7, len(l2txs))
|
||||||
|
|
||||||
// l1txs
|
// l1txs
|
||||||
assert.Equal(t, common.TxTypeCreateAccountDeposit, l1txs[0].Type)
|
assert.Equal(t, common.TxTypeCreateAccountDeposit, l1txs[0].Type)
|
||||||
assert.Equal(t, "5bac784d938067d980a9d39bdd79bf84a0cbb296977c47cc30de2d5ce9229d2f", l1txs[0].FromBJJ.String())
|
assert.Equal(t, "5bac784d938067d980a9d39bdd79bf84a0cbb296977c47cc30de2d5ce9229d2f", l1txs[0].FromBJJ.String())
|
||||||
assert.Equal(t, "5bac784d938067d980a9d39bdd79bf84a0cbb296977c47cc30de2d5ce9229d2f", l1txs[1].FromBJJ.String())
|
assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l1txs[1].FromBJJ.String())
|
||||||
assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l1txs[2].FromBJJ.String())
|
assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l1txs[2].FromBJJ.String())
|
||||||
assert.Equal(t, "a25c7150609ecfcf90fc3f419474e8bc28ea5978df1b0a68339bff884c117e19", l1txs[4].FromBJJ.String())
|
assert.Equal(t, "b6856a87832b182e5a9a1e738dbcd1f3c728bbc67ea1010aaff563eb5316131b", l1txs[4].FromBJJ.String())
|
||||||
|
|
||||||
// l2txs
|
// l2txs
|
||||||
assert.Equal(t, common.TxTypeTransfer, l2txs[0].Type)
|
assert.Equal(t, common.TxTypeTransfer, l2txs[0].Type)
|
||||||
assert.Equal(t, common.Idx(1), l2txs[0].FromIdx)
|
assert.Equal(t, common.Idx(1), l2txs[0].FromIdx)
|
||||||
assert.Equal(t, common.Idx(2), l2txs[0].ToIdx)
|
assert.Equal(t, common.Idx(3), l2txs[0].ToIdx)
|
||||||
assert.Equal(t, "323ff10c28df37ecb787fe216e111db64aa7cfa2c517509fe0057ff08a10b30c", l2txs[0].ToBJJ.String())
|
assert.Equal(t, "f3587ad5cc7414a47545770b6c75bc71930f63c491eb2294dde8b8a6670b8e96", l2txs[0].ToBJJ.String())
|
||||||
assert.Equal(t, "0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF", l2txs[0].ToEthAddr.Hex())
|
assert.Equal(t, "0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69", l2txs[0].ToEthAddr.Hex())
|
||||||
assert.Equal(t, uint64(0), l2txs[0].Nonce)
|
assert.Equal(t, common.Nonce(0), l2txs[0].Nonce)
|
||||||
assert.Equal(t, uint64(1), l2txs[3].Nonce)
|
assert.Equal(t, common.Nonce(1), l2txs[3].Nonce)
|
||||||
assert.Equal(t, common.FeeSelector(1), l2txs[0].Fee)
|
assert.Equal(t, common.FeeSelector(1), l2txs[0].Fee)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
@@ -13,9 +14,15 @@ var (
|
|||||||
// Float16 represents a float in a 16 bit format
|
// Float16 represents a float in a 16 bit format
|
||||||
type Float16 uint16
|
type Float16 uint16
|
||||||
|
|
||||||
// BigInt converts the Float16 to a big.Int integer
|
// Bytes return a byte array of length 2 with the Float16 value encoded in LittleEndian
|
||||||
func (fl16 *Float16) BigInt() *big.Int {
|
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)
|
fl := int64(*fl16)
|
||||||
|
|
||||||
m := big.NewInt(fl & 0x3FF)
|
m := big.NewInt(fl & 0x3FF)
|
||||||
@@ -30,14 +37,11 @@ func (fl16 *Float16) BigInt() *big.Int {
|
|||||||
res.Add(res, exp.Div(exp, big.NewInt(2)))
|
res.Add(res, exp.Div(exp, big.NewInt(2)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// floorFix2Float converts a fix to a float, always rounding down
|
// floorFix2Float converts a fix to a float, always rounding down
|
||||||
func floorFix2Float(_f *big.Int) Float16 {
|
func floorFix2Float(_f *big.Int) Float16 {
|
||||||
|
|
||||||
zero := big.NewInt(0)
|
zero := big.NewInt(0)
|
||||||
ten := big.NewInt(10)
|
ten := big.NewInt(10)
|
||||||
e := int64(0)
|
e := int64(0)
|
||||||
@@ -60,13 +64,11 @@ func floorFix2Float(_f *big.Int) Float16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Float16(m.Int64() | e<<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.
|
// of loss during the encoding.
|
||||||
func NewFloat16(f *big.Int) (Float16, error) {
|
func NewFloat16(f *big.Int) (Float16, error) {
|
||||||
|
|
||||||
fl1 := floorFix2Float(f)
|
fl1 := floorFix2Float(f)
|
||||||
fi1 := fl1.BigInt()
|
fi1 := fl1.BigInt()
|
||||||
fl2 := fl1 | 0x400
|
fl2 := fl1 | 0x400
|
||||||
@@ -101,20 +103,16 @@ func NewFloat16(f *big.Int) (Float16, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do rounding check
|
// Do rounding check
|
||||||
|
|
||||||
if res.BigInt().Cmp(f) == 0 {
|
if res.BigInt().Cmp(f) == 0 {
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, ErrRoundingLoss
|
return res, ErrRoundingLoss
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFloat16Floor encodes a big.Int integer as a Float16, rounding down in
|
// NewFloat16Floor encodes a big.Int integer as a Float16, rounding down in
|
||||||
// case of loss during the encoding.
|
// case of loss during the encoding.
|
||||||
func NewFloat16Floor(f *big.Int) Float16 {
|
func NewFloat16Floor(f *big.Int) Float16 {
|
||||||
|
|
||||||
fl1 := floorFix2Float(f)
|
fl1 := floorFix2Float(f)
|
||||||
fl2 := fl1 | 0x400
|
fl2 := fl1 | 0x400
|
||||||
fi2 := fl2.BigInt()
|
fi2 := fl2.BigInt()
|
||||||
@@ -123,5 +121,4 @@ func NewFloat16Floor(f *big.Int) Float16 {
|
|||||||
return fl2
|
return fl2
|
||||||
}
|
}
|
||||||
return fl1
|
return fl1
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user