Skip to content

Commit

Permalink
add support to pre-EIP155 txs (0xPolygonHermez#1582)
Browse files Browse the repository at this point in the history
* add support to pre-EIP155 txs

* =)

* naming things

* add pre-EIP-155 tx e2e test

* fix merge with develop

* add check for the vitualized batch when testing pre-EIP155 tx
  • Loading branch information
tclemos authored Jan 27, 2023
1 parent 47d1234 commit c224bb5
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 21 deletions.
1 change: 1 addition & 0 deletions ci/e2e-group1/preEIP155_test.go
3 changes: 2 additions & 1 deletion pool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ func (p *Pool) IsTxPending(ctx context.Context, hash common.Hash) (bool, error)

func (p *Pool) validateTx(ctx context.Context, tx types.Transaction) error {
// check chain id
if tx.ChainId().Uint64() != p.chainID {
txChainID := tx.ChainId().Uint64()
if txChainID != p.chainID && txChainID != 0 {
return ErrInvalidChainID
}

Expand Down
92 changes: 92 additions & 0 deletions pool/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,98 @@ func Test_AddTx(t *testing.T) {
assert.Equal(t, 1, c, "invalid number of txs in the pool")
}

func Test_AddPreEIP155Tx(t *testing.T) {
initOrResetDB()

stateSqlDB, err := db.NewSQLDB(stateDBCfg)
if err != nil {
panic(err)
}
defer stateSqlDB.Close() //nolint:gosec,errcheck

poolSqlDB, err := db.NewSQLDB(poolDBCfg)
if err != nil {
t.Error(err)
}
defer poolSqlDB.Close() //nolint:gosec,errcheck

st := newState(stateSqlDB)

genesisBlock := state.Block{
BlockNumber: 0,
BlockHash: state.ZeroHash,
ParentHash: state.ZeroHash,
ReceivedAt: time.Now(),
}
genesis := state.Genesis{
Actions: []*state.GenesisAction{
{
Address: "0xb48cA794d49EeC406A5dD2c547717e37b5952a83",
Type: int(merkletree.LeafTypeBalance),
Value: "1000000000000000000000",
},
{
Address: "0x4d5Cf5032B2a844602278b01199ED191A86c93ff",
Type: int(merkletree.LeafTypeBalance),
Value: "200000000000000000000",
},
},
}
ctx := context.Background()
dbTx, err := st.BeginStateTransaction(ctx)
require.NoError(t, err)
_, err = st.SetGenesis(ctx, genesisBlock, genesis, dbTx)
require.NoError(t, err)
require.NoError(t, dbTx.Commit(ctx))

s, err := pgpoolstorage.NewPostgresPoolStorage(poolDBCfg)
if err != nil {
t.Error(err)
}

const chainID = 2576980377
cfg := pool.Config{
FreeClaimGasLimit: 150000,
}
p := pool.NewPool(cfg, s, st, common.Address{}, chainID)

batchL2Data := "0xe580843b9aca00830186a0941275fbb540c8efc58b812ba83b0d0b8b9917ae98808464fbb77c6b39bdc5f8e458aba689f2a1ff8c543a94e4817bda40f3fe34080c4ab26c1e3c2fc2cda93bc32f0a79940501fd505dcf48d94abfde932ebf1417f502cb0d9de81b"
b, err := hex.DecodeHex(batchL2Data)
require.NoError(t, err)
txs, _, err := state.DecodeTxs(b)
require.NoError(t, err)

tx := txs[0]

err = p.AddTx(ctx, tx)
require.NoError(t, err)

rows, err := poolSqlDB.Query(ctx, "SELECT hash, encoded, decoded, status FROM pool.txs")
defer rows.Close() // nolint:staticcheck
require.NoError(t, err)

c := 0
for rows.Next() {
var hash, encoded, decoded, status string
err := rows.Scan(&hash, &encoded, &decoded, &status)
require.NoError(t, err)

b, err := tx.MarshalBinary()
require.NoError(t, err)

bJSON, err := tx.MarshalJSON()
require.NoError(t, err)

assert.Equal(t, tx.Hash().String(), hash, "invalid hash")
assert.Equal(t, hex.EncodeToHex(b), encoded, "invalid encoded")
assert.JSONEq(t, string(bJSON), decoded, "invalid decoded")
assert.Equal(t, string(pool.TxStatusPending), status, "invalid tx status")
c++
}

assert.Equal(t, 1, c, "invalid number of txs in the pool")
}

func Test_GetPendingTxs(t *testing.T) {
initOrResetDB()

Expand Down
7 changes: 5 additions & 2 deletions state/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ var (
func CheckSignature(tx types.Transaction) error {
// Check Signature
v, r, s := tx.RawSignatureValues()
plainV := byte(v.Uint64() - 35 - 2*(tx.ChainId().Uint64()))

plainV := byte(0)
chainID := tx.ChainId().Uint64()
if chainID != 0 {
plainV = byte(v.Uint64() - 35 - 2*(chainID))
}
if !crypto.ValidateSignatureValues(plainV, r, s, false) {
return ErrInvalidSig
}
Expand Down
55 changes: 38 additions & 17 deletions state/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)

const ether155V = 27
const (
double = 2
ether155V = 27
etherPre155V = 35
)

// EncodeTransactions RLP encodes the given transactions.
func EncodeTransactions(txs []types.Transaction) ([]byte, error) {
Expand All @@ -25,15 +29,22 @@ func EncodeTransactions(txs []types.Transaction) ([]byte, error) {
nonce, gasPrice, gas, to, value, data, chainID := tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data(), tx.ChainId()
log.Debug(nonce, " ", gasPrice, " ", gas, " ", to, " ", value, " ", len(data), " ", chainID)

txCodedRlp, err := rlp.EncodeToBytes([]interface{}{
rlpFieldsToEncode := []interface{}{
nonce,
gasPrice,
gas,
to,
value,
data,
chainID, uint(0), uint(0),
})
}

if tx.ChainId().Uint64() > 0 {
rlpFieldsToEncode = append(rlpFieldsToEncode, chainID)
rlpFieldsToEncode = append(rlpFieldsToEncode, uint(0))
rlpFieldsToEncode = append(rlpFieldsToEncode, uint(0))
}

txCodedRlp, err := rlp.EncodeToBytes(rlpFieldsToEncode)

if err != nil {
return nil, err
Expand Down Expand Up @@ -91,7 +102,7 @@ func EncodeUnsignedTransaction(tx types.Transaction, chainID uint64) ([]byte, er
return txData, nil
}

// DecodeTxs extracts Tansactions for its encoded form
// DecodeTxs extracts Transactions for its encoded form
func DecodeTxs(txsData []byte) ([]types.Transaction, []byte, error) {
// Process coded txs
var pos int64
Expand All @@ -105,7 +116,6 @@ func DecodeTxs(txsData []byte) ([]types.Transaction, []byte, error) {
ff = 255 // max value of rlp header
shortRlp = 55 // length of the short rlp codification
f7 = 247 // 192 + 55 = c0 + shortRlp
etherNewV = 35
mul2 = 2
)
txDataLength := len(txsData)
Expand All @@ -132,26 +142,37 @@ func DecodeTxs(txsData []byte) ([]types.Transaction, []byte, error) {

fullDataTx := txsData[pos : pos+len+rLength+sLength+vLength+headerByteLength]
txInfo := txsData[pos : pos+len+headerByteLength]
r := txsData[pos+len+headerByteLength : pos+len+rLength+headerByteLength]
s := txsData[pos+len+rLength+headerByteLength : pos+len+rLength+sLength+headerByteLength]
v := txsData[pos+len+rLength+sLength+headerByteLength : pos+len+rLength+sLength+vLength+headerByteLength]
rData := txsData[pos+len+headerByteLength : pos+len+rLength+headerByteLength]
sData := txsData[pos+len+rLength+headerByteLength : pos+len+rLength+sLength+headerByteLength]
vData := txsData[pos+len+rLength+sLength+headerByteLength : pos+len+rLength+sLength+vLength+headerByteLength]

pos = pos + len + rLength + sLength + vLength + headerByteLength

// Decode tx
var tx types.LegacyTx
err = rlp.DecodeBytes(txInfo, &tx)
// Decode rlpFields
var rlpFields [][]byte
err = rlp.DecodeBytes(txInfo, &rlpFields)
if err != nil {
log.Debug("error decoding tx bytes: ", err, ". fullDataTx: ", hex.EncodeToString(fullDataTx), "\n tx: ", hex.EncodeToString(txInfo), "\n Txs received: ", hex.EncodeToString(txsData))
return []types.Transaction{}, []byte{}, err
}

//tx.V = v-27+chainId*2+35
tx.V = new(big.Int).Add(new(big.Int).Sub(new(big.Int).SetBytes(v), big.NewInt(ether155V)), new(big.Int).Add(new(big.Int).Mul(tx.V, big.NewInt(mul2)), big.NewInt(etherNewV)))
tx.R = new(big.Int).SetBytes(r)
tx.S = new(big.Int).SetBytes(s)
legacyTx, chainID, err := RlpFieldsToLegacyTx(rlpFields)
if err != nil {
log.Debug("error creating tx from rlp fields: ", err, ". fullDataTx: ", hex.EncodeToString(fullDataTx), "\n tx: ", hex.EncodeToString(txInfo), "\n Txs received: ", hex.EncodeToString(txsData))
return []types.Transaction{}, []byte{}, err
}

if chainID != nil {
//v = v-27+chainId*2+35
legacyTx.V = new(big.Int).Add(new(big.Int).Sub(new(big.Int).SetBytes(vData), big.NewInt(ether155V)), new(big.Int).Add(new(big.Int).Mul(chainID, big.NewInt(mul2)), big.NewInt(etherPre155V)))
} else {
legacyTx.V = new(big.Int).SetBytes(vData)
}
legacyTx.R = new(big.Int).SetBytes(rData)
legacyTx.S = new(big.Int).SetBytes(sData)

txs = append(txs, *types.NewTx(&tx))
tx := types.NewTx(legacyTx)
txs = append(txs, *tx)
}
return txs, txsData, nil
}
Expand Down
70 changes: 70 additions & 0 deletions state/transaction.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package state

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
Expand All @@ -14,3 +16,71 @@ func GetSender(tx types.Transaction) (common.Address, error) {
}
return sender, nil
}

// RlpFieldsToLegacyTx parses the rlp fields slice into a type.LegacyTx
// in this specific order:
//
// required fields:
// [0] Nonce uint64
// [1] GasPrice *big.Int
// [2] Gas uint64
// [3] To *common.Address
// [4] Value *big.Int
// [5] Data []byte
//
// optional fields:
// [6] V *big.Int
// [7] R *big.Int
// [8] S *big.Int
func RlpFieldsToLegacyTx(fields [][]byte) (tx *types.LegacyTx, chainID *big.Int, err error) {
const (
fieldsSizeWithoutChainID = 6
fieldsSizeWithV = 7
fieldsSizeWithVR = 8
fieldsSizeWithVRS = 9
)

if len(fields) != fieldsSizeWithoutChainID && len(fields) != fieldsSizeWithV && len(fields) != fieldsSizeWithVRS {
return nil, nil, types.ErrTxTypeNotSupported
}

nonce := big.NewInt(0).SetBytes(fields[0]).Uint64()
gasPrice := big.NewInt(0).SetBytes(fields[1])
gas := big.NewInt(0).SetBytes(fields[2]).Uint64()
var to *common.Address
if fields[3] != nil {
tmp := common.BytesToAddress(fields[3])
to = &tmp
}
value := big.NewInt(0).SetBytes(fields[4])
data := fields[5]

v := big.NewInt(0)
if len(fields) >= fieldsSizeWithV {
v = big.NewInt(0).SetBytes(fields[6])
chainID = big.NewInt(0).Sub(v, big.NewInt(0).SetUint64(etherPre155V))
chainID = big.NewInt(0).Quo(chainID, big.NewInt(double))
}

r := big.NewInt(0)
if len(fields) >= fieldsSizeWithVR {
r = big.NewInt(0).SetBytes(fields[7])
}

s := big.NewInt(0)
if len(fields) >= fieldsSizeWithVRS {
s = big.NewInt(0).SetBytes(fields[8])
}

return &types.LegacyTx{
Nonce: nonce,
GasPrice: gasPrice,
Gas: gas,
To: to,
Value: value,
Data: data,
V: v,
R: r,
S: s,
}, chainID, nil
}
2 changes: 1 addition & 1 deletion test/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ services:
ports:
- 8545:8545
- 8546:8546
command: ["--http", "--http.api", "admin,eth,debug,miner,net,txpool,personal,web3", "--http.addr", "0.0.0.0","--http.corsdomain", "*", "--http.vhosts" ,"*", "--ws", "--ws.origins", "*", "--ws.addr", "0.0.0.0", "--dev", "--datadir", "/geth_data", "--syncmode", "full"]
command: ["--http", "--http.api", "admin,eth,debug,miner,net,txpool,personal,web3", "--http.addr", "0.0.0.0","--http.corsdomain", "*", "--http.vhosts" ,"*", "--ws", "--ws.origins", "*", "--ws.addr", "0.0.0.0", "--dev", "--datadir", "/geth_data", "--syncmode", "full", "--rpc.allow-unprotected-txs"]

zkevm-prover:
container_name: zkevm-prover
Expand Down
7 changes: 7 additions & 0 deletions test/e2e/debug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package e2e

import "testing"

func TestDebugTraceTransaction(t *testing.T) {

}
Loading

0 comments on commit c224bb5

Please sign in to comment.