mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 03:16:45 +01:00
Add methods for ZKInputs IntermStates generation
- Add L1Tx TxCompressedData method - Add PoolL2Tx TxCompressedDataV2 method - Update ProcessTxs logic - Add ZKInputs Intermediate States & Fee parameters calculation
This commit is contained in:
@@ -6,6 +6,16 @@ import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// MaxFeePlan is the maximum value of the FeePlan
|
||||
const MaxFeePlan = 256
|
||||
|
||||
// FeePlan represents the fee model, a position in the array indicates the
|
||||
// percentage of tokens paid in concept of fee for a transaction
|
||||
var FeePlan = [MaxFeePlan]float64{}
|
||||
|
||||
// FeeFactorLsh60 is the feeFactor << 60
|
||||
var FeeFactorLsh60 [256]*big.Int
|
||||
|
||||
// RecommendedFee is the recommended fee to pay in USD per transaction set by
|
||||
// the coordinator according to the tx type (if the tx requires to create an
|
||||
// account and register, only register or he account already esists)
|
||||
@@ -31,13 +41,6 @@ func (f FeeSelector) Percentage() float64 {
|
||||
}
|
||||
}
|
||||
|
||||
// MaxFeePlan is the maximum value of the FeePlan
|
||||
const MaxFeePlan = 256
|
||||
|
||||
// FeePlan represents the fee model, a position in the array indicates the
|
||||
// percentage of tokens paid in concept of fee for a transaction
|
||||
var FeePlan = [MaxFeePlan]float64{}
|
||||
|
||||
// CalcFeeAmount calculates the fee amount in tokens from an amount and
|
||||
// feeSelector (fee index).
|
||||
func CalcFeeAmount(amount *big.Int, feeSel FeeSelector) (*big.Int, error) {
|
||||
@@ -55,9 +58,6 @@ func init() {
|
||||
setFeeFactorLsh60(&FeeFactorLsh60)
|
||||
}
|
||||
|
||||
// FeeFactorLsh60 is the feeFactor << 60
|
||||
var FeeFactorLsh60 [256]*big.Int
|
||||
|
||||
func setFeeFactorLsh60(feeFactorLsh60 *[256]*big.Int) {
|
||||
feeFactorLsh60[0], _ = new(big.Int).SetString("0", 10)
|
||||
feeFactorLsh60[1], _ = new(big.Int).SetString("3", 10)
|
||||
|
||||
@@ -146,6 +146,43 @@ func (tx L1Tx) Tx() Tx {
|
||||
return genericTx
|
||||
}
|
||||
|
||||
// TxCompressedData spec:
|
||||
// [ 1 bits ] empty (toBJJSign) // 1 byte
|
||||
// [ 8 bits ] empty (userFee) // 1 byte
|
||||
// [ 40 bits ] empty (nonce) // 5 bytes
|
||||
// [ 32 bits ] tokenID // 4 bytes
|
||||
// [ 16 bits ] amountFloat16 // 2 bytes
|
||||
// [ 48 bits ] toIdx // 6 bytes
|
||||
// [ 48 bits ] fromIdx // 6 bytes
|
||||
// [ 16 bits ] chainId // 2 bytes
|
||||
// [ 32 bits ] empty (signatureConstant) // 4 bytes
|
||||
// Total bits compressed data: 241 bits // 31 bytes in *big.Int representation
|
||||
func (tx L1Tx) TxCompressedData() (*big.Int, error) {
|
||||
amountFloat16, err := NewFloat16(tx.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var b [31]byte
|
||||
// b[0:7] empty: no fee neither nonce
|
||||
copy(b[7:11], tx.TokenID.Bytes())
|
||||
copy(b[11:13], amountFloat16.Bytes())
|
||||
toIdxBytes, err := tx.ToIdx.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[13:19], toIdxBytes[:])
|
||||
fromIdxBytes, err := tx.FromIdx.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[19:25], fromIdxBytes[:])
|
||||
copy(b[25:27], []byte{0, 1}) // TODO this will be generated by the ChainID config parameter
|
||||
// b[27:] empty: no signature
|
||||
|
||||
bi := new(big.Int).SetBytes(b[:])
|
||||
return bi, nil
|
||||
}
|
||||
|
||||
// BytesGeneric returns the generic representation of a L1Tx. This method is
|
||||
// used to compute the []byte representation of a L1UserTx, and also to compute
|
||||
// the L1TxData for the ZKInputs (at the HashGlobalInputs), using this method
|
||||
|
||||
@@ -49,6 +49,22 @@ func TestNewL1CoordinatorTx(t *testing.T) {
|
||||
assert.Equal(t, "0x01000000000000cafe005800", l1Tx.TxID.String())
|
||||
}
|
||||
|
||||
func TestL1TxCompressedData(t *testing.T) {
|
||||
tx := L1Tx{
|
||||
FromIdx: 2,
|
||||
ToIdx: 3,
|
||||
Amount: big.NewInt(4),
|
||||
TokenID: 5,
|
||||
}
|
||||
txCompressedData, err := tx.TxCompressedData()
|
||||
assert.Nil(t, err)
|
||||
|
||||
// test vector value generated from javascript implementation
|
||||
expectedStr := "7307597389635308713748674793997299267460566876160"
|
||||
assert.Equal(t, expectedStr, txCompressedData.String())
|
||||
assert.Equal(t, "050004000000000003000000000002000100000000", hex.EncodeToString(txCompressedData.Bytes()))
|
||||
}
|
||||
|
||||
func TestL1userTxByteParsers(t *testing.T) {
|
||||
var pkComp babyjub.PublicKeyComp
|
||||
pkCompL := []byte("0x56ca90f80d7c374ae7485e9bcc47d4ac399460948da6aeeb899311097925a72c")
|
||||
|
||||
@@ -140,13 +140,64 @@ func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[19:25], fromIdxBytes[:])
|
||||
copy(b[25:27], []byte{0, 1, 0, 0}) // TODO check js implementation (unexpected behaviour from test vector generated from js)
|
||||
copy(b[25:27], []byte{0, 1}) // TODO this will be generated by the ChainID config parameter
|
||||
copy(b[27:31], sc.Bytes())
|
||||
|
||||
bi := new(big.Int).SetBytes(b[:])
|
||||
return bi, nil
|
||||
}
|
||||
|
||||
// TxCompressedDataV2 spec:
|
||||
// [ 1 bits ] toBJJSign // 1 byte
|
||||
// [ 8 bits ] userFee // 1 byte
|
||||
// [ 40 bits ] nonce // 5 bytes
|
||||
// [ 32 bits ] tokenID // 4 bytes
|
||||
// [ 16 bits ] amountFloat16 // 2 bytes
|
||||
// [ 48 bits ] toIdx // 6 bytes
|
||||
// [ 48 bits ] fromIdx // 6 bytes
|
||||
// Total bits compressed data: 193 bits // 25 bytes in *big.Int representation
|
||||
func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) {
|
||||
if tx.Amount == nil {
|
||||
tx.Amount = big.NewInt(0)
|
||||
}
|
||||
amountFloat16, err := NewFloat16(tx.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var b [25]byte
|
||||
toBJJSign := byte(0)
|
||||
if tx.ToBJJ != nil && babyjub.PointCoordSign(tx.ToBJJ.X) {
|
||||
toBJJSign = byte(1)
|
||||
}
|
||||
b[0] = toBJJSign
|
||||
b[1] = byte(tx.Fee)
|
||||
nonceBytes, err := tx.Nonce.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[2:7], nonceBytes[:])
|
||||
copy(b[7:11], tx.TokenID.Bytes())
|
||||
copy(b[11:13], amountFloat16.Bytes())
|
||||
toIdxBytes, err := tx.ToIdx.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[13:19], toIdxBytes[:])
|
||||
fromIdxBytes, err := tx.FromIdx.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b[19:25], fromIdxBytes[:])
|
||||
|
||||
bi := new(big.Int).SetBytes(b[:])
|
||||
return bi, nil
|
||||
}
|
||||
|
||||
// RqTxCompressedDataV2 is like the TxCompressedDataV2 but using the 'Rq'
|
||||
// parameters. In a future iteration of the hermez-node, the 'Rq' parameters
|
||||
// can be inside a struct, which contains the 'Rq' transaction grouped inside,
|
||||
// so then computing the 'RqTxCompressedDataV2' would be just calling
|
||||
// 'tx.Rq.TxCompressedDataV2()'.
|
||||
// RqTxCompressedDataV2 spec:
|
||||
// [ 1 bits ] rqToBJJSign // 1 byte
|
||||
// [ 8 bits ] rqUserFee // 1 byte
|
||||
|
||||
@@ -40,10 +40,7 @@ func TestTxCompressedData(t *testing.T) {
|
||||
// test vector value generated from javascript implementation
|
||||
expectedStr := "1766847064778421992193717128424891165872736891548909569553540449389241871"
|
||||
assert.Equal(t, expectedStr, txCompressedData.String())
|
||||
expected, ok := new(big.Int).SetString(expectedStr, 10)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, expected.Bytes(), txCompressedData.Bytes())
|
||||
assert.Equal(t, "10000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes())[1:])
|
||||
assert.Equal(t, "010000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes()))
|
||||
tx = PoolL2Tx{
|
||||
RqFromIdx: 7,
|
||||
RqToIdx: 8,
|
||||
@@ -58,10 +55,56 @@ func TestTxCompressedData(t *testing.T) {
|
||||
// test vector value generated from javascript implementation
|
||||
expectedStr = "6571340879233176732837827812956721483162819083004853354503"
|
||||
assert.Equal(t, expectedStr, txCompressedData.String())
|
||||
expected, ok = new(big.Int).SetString(expectedStr, 10)
|
||||
assert.Equal(t, "010c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes()))
|
||||
}
|
||||
|
||||
func TestTxCompressedDataV2(t *testing.T) {
|
||||
var sk babyjub.PrivateKey
|
||||
_, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
|
||||
assert.Nil(t, err)
|
||||
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
|
||||
expectedStr := "6571340879233176732837827812956721483162819083004853354503"
|
||||
assert.Equal(t, expectedStr, txCompressedData.String())
|
||||
expected, ok := new(big.Int).SetString(expectedStr, 10)
|
||||
assert.True(t, ok)
|
||||
|
||||
assert.Equal(t, expected.Bytes(), txCompressedData.Bytes())
|
||||
assert.Equal(t, "010c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes()))
|
||||
}
|
||||
|
||||
func TestRqTxCompressedDataV2(t *testing.T) {
|
||||
var sk babyjub.PrivateKey
|
||||
_, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
|
||||
assert.Nil(t, err)
|
||||
tx := PoolL2Tx{
|
||||
RqFromIdx: 7,
|
||||
RqToIdx: 8,
|
||||
RqAmount: big.NewInt(9),
|
||||
RqTokenID: 10,
|
||||
RqNonce: 11,
|
||||
RqFee: 12,
|
||||
RqToBJJ: sk.Public(),
|
||||
}
|
||||
txCompressedData, err := tx.RqTxCompressedDataV2()
|
||||
assert.Nil(t, err)
|
||||
// test vector value generated from javascript implementation
|
||||
expectedStr := "6571340879233176732837827812956721483162819083004853354503"
|
||||
assert.Equal(t, expectedStr, txCompressedData.String())
|
||||
expected, ok := new(big.Int).SetString(expectedStr, 10)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, expected.Bytes(), txCompressedData.Bytes())
|
||||
assert.Equal(t, "10c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes())[1:])
|
||||
assert.Equal(t, "010c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes()))
|
||||
}
|
||||
|
||||
func TestHashToSign(t *testing.T) {
|
||||
|
||||
@@ -52,3 +52,8 @@ func TokenIDFromBytes(b []byte) (TokenID, error) {
|
||||
tid := binary.BigEndian.Uint32(b[:4])
|
||||
return TokenID(tid), nil
|
||||
}
|
||||
|
||||
// TokenIDFromBigInt returns a TokenID with the value of the given *big.Int
|
||||
func TokenIDFromBigInt(b *big.Int) TokenID {
|
||||
return TokenID(b.Int64())
|
||||
}
|
||||
|
||||
19
common/zk.go
19
common/zk.go
@@ -43,7 +43,7 @@ type ZKMetadata struct {
|
||||
|
||||
// ZKInputs represents the inputs that will be used to generate the zkSNARK proof
|
||||
type ZKInputs struct {
|
||||
Metadata ZKMetadata
|
||||
Metadata ZKMetadata `json:"-"`
|
||||
|
||||
//
|
||||
// General
|
||||
@@ -183,23 +183,28 @@ type ZKInputs struct {
|
||||
// will not be used.
|
||||
|
||||
// decode-tx
|
||||
// ISOnChain indicates if tx is L1 (true) or L2 (false)
|
||||
// ISOnChain indicates if tx is L1 (true (1)) or L2 (false (0))
|
||||
ISOnChain []*big.Int `json:"imOnChain"` // bool, len: [nTx - 1]
|
||||
// ISOutIdx current index account for each Tx
|
||||
// Contains the index of the created account in case that the tx is of
|
||||
// account creation type.
|
||||
ISOutIdx []*big.Int `json:"imOutIdx"` // uint64 (max nLevels bits), len: [nTx - 1]
|
||||
// rollup-tx
|
||||
// ISStateRoot root at the moment of the Tx, the state root value once the Tx is processed into the state tree
|
||||
// ISStateRoot root at the moment of the Tx (once processed), the state root value once the Tx is processed into the state tree
|
||||
ISStateRoot []*big.Int `json:"imStateRoot"` // Hash, len: [nTx - 1]
|
||||
// ISExitTree root at the moment of the Tx the value once the Tx is processed into the exit tree
|
||||
// ISExitTree root at the moment (once processed) of the Tx the value
|
||||
// once the Tx is processed into the exit tree
|
||||
ISExitRoot []*big.Int `json:"imExitRoot"` // Hash, len: [nTx - 1]
|
||||
// ISAccFeeOut accumulated fees once the Tx is processed
|
||||
// ISAccFeeOut accumulated fees once the Tx is processed. Contains the
|
||||
// array of FeeAccount Balances at each moment of each Tx processed.
|
||||
ISAccFeeOut [][]*big.Int `json:"imAccFeeOut"` // big.Int, len: [nTx - 1][maxFeeIdxs]
|
||||
// fee-tx
|
||||
// ISStateRootFee root at the moment of the Tx, the state root value once the Tx is processed into the state tree
|
||||
// ISStateRootFee root at the moment of the Tx (once processed), the state root value once the Tx is processed into the state tree
|
||||
ISStateRootFee []*big.Int `json:"imStateRootFee"` // Hash, len: [maxFeeIdxs - 1]
|
||||
// ISInitStateRootFee state root once all L1-L2 tx are processed (before computing the fees-tx)
|
||||
ISInitStateRootFee *big.Int `json:"imInitStateRootFee"` // Hash
|
||||
// ISFinalAccFee final accumulated fees (before computing the fees-tx)
|
||||
// ISFinalAccFee final accumulated fees (before computing the fees-tx).
|
||||
// Contains the final values of the ISAccFeeOut parameter
|
||||
ISFinalAccFee []*big.Int `json:"imFinalAccFee"` // big.Int, len: [maxFeeIdxs - 1]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user