mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 03:16:45 +01:00
Add TxID calculation & New{Layer}Tx Type
Add TxID calculation & New{Layer}Tx Type
New{Layer}Tx methods that compute the `TxID` & `TxType` values from the
transaction values:
- NewL1Tx
- NewL2Tx
- NewPoolL2Tx
Add TxID Scanner & Valuer for database/sql
HistoryDB & L2DB & API packages tests will need to be addapted to the
TestTransaction generation once is done.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
@@ -16,6 +17,13 @@ const (
|
||||
// L1Tx is a struct that represents a L1 tx
|
||||
type L1Tx struct {
|
||||
// Stored in DB: mandatory fileds
|
||||
|
||||
// TxID (12 bytes) for L1Tx is:
|
||||
// bytes: | 1 | 8 | 2 | 1 |
|
||||
// values: | type | ToForgeL1TxsNum | Position | 0 (padding) |
|
||||
// where type:
|
||||
// - L1UserTx: 0
|
||||
// - L1CoordinatorTx: 1
|
||||
TxID TxID
|
||||
ToForgeL1TxsNum int64 // toForgeL1TxsNum in which the tx was forged / will be forged
|
||||
Position int
|
||||
@@ -34,6 +42,58 @@ type L1Tx struct {
|
||||
LoadAmountUSD *float64
|
||||
}
|
||||
|
||||
// NewL1Tx returns the given L1Tx with the TxId & Type parameters calculated
|
||||
// from the L1Tx values
|
||||
func NewL1Tx(l1Tx *L1Tx) (*L1Tx, error) {
|
||||
// calculate TxType
|
||||
var txType TxType
|
||||
if l1Tx.FromIdx == Idx(0) {
|
||||
if l1Tx.ToIdx == Idx(0) {
|
||||
txType = TxTypeCreateAccountDeposit
|
||||
} else if l1Tx.ToIdx >= IdxUserThreshold {
|
||||
txType = TxTypeCreateAccountDepositTransfer
|
||||
} else {
|
||||
return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid ToIdx value: %d", l1Tx.ToIdx)
|
||||
}
|
||||
} else if l1Tx.FromIdx >= IdxUserThreshold {
|
||||
if l1Tx.ToIdx == Idx(0) {
|
||||
txType = TxTypeDeposit
|
||||
} else if l1Tx.ToIdx == Idx(1) {
|
||||
txType = TxTypeExit
|
||||
} else if l1Tx.ToIdx >= IdxUserThreshold {
|
||||
if l1Tx.LoadAmount.Int64() == int64(0) {
|
||||
txType = TxTypeForceTransfer
|
||||
} else {
|
||||
txType = TxTypeDepositTransfer
|
||||
}
|
||||
} else {
|
||||
return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid ToIdx value: %d", l1Tx.ToIdx)
|
||||
}
|
||||
} else {
|
||||
return l1Tx, fmt.Errorf("Can not determine type of L1Tx, invalid FromIdx value: %d", l1Tx.FromIdx)
|
||||
}
|
||||
|
||||
if l1Tx.Type != "" && l1Tx.Type != txType {
|
||||
return l1Tx, fmt.Errorf("L1Tx.Type: %s, should be: %s", l1Tx.Type, txType)
|
||||
}
|
||||
l1Tx.Type = txType
|
||||
|
||||
var txid [TxIDLen]byte
|
||||
if !l1Tx.UserOrigin {
|
||||
txid[0] = TxIDPrefixL1CoordTx
|
||||
}
|
||||
var toForgeL1TxsNumBytes [8]byte
|
||||
binary.BigEndian.PutUint64(toForgeL1TxsNumBytes[:], uint64(l1Tx.ToForgeL1TxsNum))
|
||||
copy(txid[1:9], toForgeL1TxsNumBytes[:])
|
||||
|
||||
var positionBytes [2]byte
|
||||
binary.BigEndian.PutUint16(positionBytes[:], uint16(l1Tx.Position))
|
||||
copy(txid[9:11], positionBytes[:])
|
||||
l1Tx.TxID = TxID(txid)
|
||||
|
||||
return l1Tx, nil
|
||||
}
|
||||
|
||||
// Tx returns a *Tx from the L1Tx
|
||||
func (tx *L1Tx) Tx() *Tx {
|
||||
f := new(big.Float).SetInt(tx.Amount)
|
||||
|
||||
@@ -11,6 +11,21 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewL1Tx(t *testing.T) {
|
||||
l1Tx := &L1Tx{
|
||||
ToForgeL1TxsNum: int64(123456),
|
||||
Position: 71,
|
||||
ToIdx: 301,
|
||||
TokenID: 5,
|
||||
Amount: big.NewInt(1),
|
||||
LoadAmount: big.NewInt(2),
|
||||
FromIdx: 300,
|
||||
}
|
||||
l1Tx, err := NewL1Tx(l1Tx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "0x01000000000001e240004700", l1Tx.TxID.String())
|
||||
}
|
||||
|
||||
func TestL1TxByteParsers(t *testing.T) {
|
||||
var pkComp babyjub.PublicKeyComp
|
||||
err := pkComp.UnmarshalText([]byte("0x56ca90f80d7c374ae7485e9bcc47d4ac399460948da6aeeb899311097925a72c"))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
@@ -21,6 +22,42 @@ type L2Tx struct {
|
||||
EthBlockNum int64 // Ethereum Block Number in which this L2Tx was added to the queue
|
||||
}
|
||||
|
||||
// NewL2Tx returns the given L2Tx with the TxId & Type parameters calculated
|
||||
// from the L2Tx values
|
||||
func NewL2Tx(l2Tx *L2Tx) (*L2Tx, error) {
|
||||
// calculate TxType
|
||||
var txType TxType
|
||||
if l2Tx.ToIdx == Idx(1) {
|
||||
txType = TxTypeExit
|
||||
} else if l2Tx.ToIdx >= IdxUserThreshold {
|
||||
txType = TxTypeTransfer
|
||||
} else {
|
||||
return l2Tx, fmt.Errorf("Can not determine type of L2Tx, invalid ToIdx value: %d", l2Tx.ToIdx)
|
||||
}
|
||||
|
||||
// if TxType!=l2Tx.TxType return error
|
||||
if l2Tx.Type != "" && l2Tx.Type != txType {
|
||||
return l2Tx, fmt.Errorf("L2Tx.Type: %s, should be: %s", l2Tx.Type, txType)
|
||||
}
|
||||
l2Tx.Type = txType
|
||||
|
||||
var txid [TxIDLen]byte
|
||||
txid[0] = TxIDPrefixL2Tx
|
||||
fromIdxBytes, err := l2Tx.FromIdx.Bytes()
|
||||
if err != nil {
|
||||
return l2Tx, err
|
||||
}
|
||||
copy(txid[1:7], fromIdxBytes[:])
|
||||
nonceBytes, err := l2Tx.Nonce.Bytes()
|
||||
if err != nil {
|
||||
return l2Tx, err
|
||||
}
|
||||
copy(txid[7:12], nonceBytes[:])
|
||||
l2Tx.TxID = TxID(txid)
|
||||
|
||||
return l2Tx, nil
|
||||
}
|
||||
|
||||
// Tx returns a *Tx from the L2Tx
|
||||
func (tx *L2Tx) Tx() *Tx {
|
||||
f := new(big.Float).SetInt(tx.Amount)
|
||||
|
||||
20
common/l2tx_test.go
Normal file
20
common/l2tx_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewL2Tx(t *testing.T) {
|
||||
l2Tx := &L2Tx{
|
||||
FromIdx: 87654,
|
||||
ToIdx: 300,
|
||||
Amount: big.NewInt(4),
|
||||
Nonce: 144,
|
||||
}
|
||||
l2Tx, err := NewL2Tx(l2Tx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "0x020000000156660000000090", l2Tx.TxID.String())
|
||||
}
|
||||
@@ -13,6 +13,10 @@ import (
|
||||
// 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
|
||||
|
||||
// TxID (12 bytes) for L2Tx is:
|
||||
// bytes: | 1 | 6 | 5 |
|
||||
// values: | type | FromIdx | Nonce |
|
||||
TxID TxID `meddler:"tx_id"`
|
||||
FromIdx Idx `meddler:"from_idx"` // FromIdx is used by L1Tx/Deposit to indicate the Idx receiver of the L1Tx.LoadAmount (deposit)
|
||||
ToIdx Idx `meddler:"to_idx"` // ToIdx is ignored in L1Tx/Deposit, but used in the L1Tx/DepositAndTransfer
|
||||
@@ -45,6 +49,44 @@ type PoolL2Tx struct {
|
||||
TokenSymbol string `meddler:"token_symbol"`
|
||||
}
|
||||
|
||||
// NewPoolL2Tx returns the given L2Tx with the TxId & Type parameters calculated
|
||||
// from the L2Tx values
|
||||
func NewPoolL2Tx(poolL2Tx *PoolL2Tx) (*PoolL2Tx, error) {
|
||||
// calculate TxType
|
||||
var txType TxType
|
||||
if poolL2Tx.ToIdx == Idx(0) {
|
||||
txType = TxTypeTransfer
|
||||
} else if poolL2Tx.ToIdx == Idx(1) {
|
||||
txType = TxTypeExit
|
||||
} else if poolL2Tx.ToIdx >= IdxUserThreshold {
|
||||
txType = TxTypeTransfer
|
||||
} else {
|
||||
return poolL2Tx, fmt.Errorf("Can not determine type of PoolL2Tx, invalid ToIdx value: %d", poolL2Tx.ToIdx)
|
||||
}
|
||||
|
||||
// if TxType!=poolL2Tx.TxType return error
|
||||
if poolL2Tx.Type != "" && poolL2Tx.Type != txType {
|
||||
return poolL2Tx, fmt.Errorf("PoolL2Tx.Type: %s, should be: %s", poolL2Tx.Type, txType)
|
||||
}
|
||||
poolL2Tx.Type = txType
|
||||
|
||||
var txid [TxIDLen]byte
|
||||
txid[0] = TxIDPrefixL2Tx
|
||||
fromIdxBytes, err := poolL2Tx.FromIdx.Bytes()
|
||||
if err != nil {
|
||||
return poolL2Tx, err
|
||||
}
|
||||
copy(txid[1:7], fromIdxBytes[:])
|
||||
nonceBytes, err := poolL2Tx.Nonce.Bytes()
|
||||
if err != nil {
|
||||
return poolL2Tx, err
|
||||
}
|
||||
copy(txid[7:12], nonceBytes[:])
|
||||
poolL2Tx.TxID = TxID(txid)
|
||||
|
||||
return poolL2Tx, nil
|
||||
}
|
||||
|
||||
// TxCompressedData spec:
|
||||
// [ 1 bits ] toBJJSign // 1 byte
|
||||
// [ 8 bits ] userFee // 1 byte
|
||||
|
||||
@@ -10,6 +10,19 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewPoolL2Tx(t *testing.T) {
|
||||
poolL2Tx := &PoolL2Tx{
|
||||
FromIdx: 87654,
|
||||
ToIdx: 300,
|
||||
Amount: big.NewInt(4),
|
||||
TokenID: 5,
|
||||
Nonce: 144,
|
||||
}
|
||||
poolL2Tx, err := NewPoolL2Tx(poolL2Tx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "0x020000000156660000000090", poolL2Tx.TxID.String())
|
||||
}
|
||||
|
||||
func TestTxCompressedData(t *testing.T) {
|
||||
var sk babyjub.PrivateKey
|
||||
_, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
|
||||
|
||||
43
common/tx.go
43
common/tx.go
@@ -1,14 +1,55 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
||||
)
|
||||
|
||||
const (
|
||||
// TXIDPrefixL1CoordTx is the prefix that determines that the TxID is
|
||||
// for a L1CoordinatorTx
|
||||
//nolinter:gomnd
|
||||
TxIDPrefixL1CoordTx = byte(1)
|
||||
|
||||
// TxIDPrefixL2Tx is the prefix that determines that the TxID is for a
|
||||
// L2Tx (or PoolL2Tx)
|
||||
//nolinter:gomnd
|
||||
TxIDPrefixL2Tx = byte(2)
|
||||
|
||||
// TxIDLen is the length of the TxID byte array
|
||||
TxIDLen = 12
|
||||
)
|
||||
|
||||
// TxID is the identifier of a Hermez network transaction
|
||||
type TxID Hash // Hash is a guess
|
||||
type TxID [TxIDLen]byte
|
||||
|
||||
// Scan implements Scanner for database/sql.
|
||||
func (txid *TxID) Scan(src interface{}) error {
|
||||
srcB, ok := src.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("can't scan %T into TxID", src)
|
||||
}
|
||||
if len(srcB) != TxIDLen {
|
||||
return fmt.Errorf("can't scan []byte of len %d into TxID, need %d", len(srcB), TxIDLen)
|
||||
}
|
||||
copy(txid[:], srcB)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements valuer for database/sql.
|
||||
func (txid TxID) Value() (driver.Value, error) {
|
||||
return txid[:], nil
|
||||
}
|
||||
|
||||
// String returns a string hexadecimal representation of the TxID
|
||||
func (txid TxID) String() string {
|
||||
return "0x" + hex.EncodeToString(txid[:])
|
||||
}
|
||||
|
||||
// TxType is a string that represents the type of a Hermez network transaction
|
||||
type TxType string
|
||||
|
||||
27
common/tx_test.go
Normal file
27
common/tx_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTxIDScannerValue(t *testing.T) {
|
||||
txid0 := &TxID{}
|
||||
txid1 := &TxID{}
|
||||
txid0B := [12]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
txid1B := [12]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
copy(txid0[:], txid0B[:])
|
||||
copy(txid1[:], txid1B[:])
|
||||
|
||||
var value driver.Valuer
|
||||
var scan sql.Scanner
|
||||
value = txid0
|
||||
scan = txid1
|
||||
fromDB, err := value.Value()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, scan.Scan(fromDB))
|
||||
assert.Equal(t, value, scan)
|
||||
}
|
||||
Reference in New Issue
Block a user