401 lines
12 KiB
Go
401 lines
12 KiB
Go
|
package itests
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/hex"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
|
||
|
"github.com/filecoin-project/go-state-types/abi"
|
||
|
"github.com/filecoin-project/go-state-types/big"
|
||
|
"github.com/filecoin-project/go-state-types/network"
|
||
|
|
||
|
"github.com/filecoin-project/lotus/build"
|
||
|
"github.com/filecoin-project/lotus/chain/consensus/filcns"
|
||
|
"github.com/filecoin-project/lotus/chain/stmgr"
|
||
|
"github.com/filecoin-project/lotus/chain/types"
|
||
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||
|
)
|
||
|
|
||
|
func TestLegacyValueTransferValidSignature(t *testing.T) {
|
||
|
blockTime := 100 * time.Millisecond
|
||
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||
|
|
||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||
|
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||
|
defer cancel()
|
||
|
|
||
|
// create a new Ethereum account
|
||
|
key, ethAddr, deployer := client.EVM().NewAccount()
|
||
|
_, ethAddr2, _ := client.EVM().NewAccount()
|
||
|
|
||
|
kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000))
|
||
|
|
||
|
gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{
|
||
|
From: ðAddr,
|
||
|
To: ðAddr2,
|
||
|
Value: ethtypes.EthBigInt(big.NewInt(100)),
|
||
|
}})
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
gaslimit, err := client.EthEstimateGas(ctx, gasParams)
|
||
|
require.NoError(t, err)
|
||
|
fmt.Println("gas limit is", gaslimit)
|
||
|
|
||
|
tx := ethtypes.EthLegacyHomesteadTxArgs{
|
||
|
Value: big.NewInt(100),
|
||
|
Nonce: 0,
|
||
|
To: ðAddr2,
|
||
|
GasPrice: types.NanoFil,
|
||
|
GasLimit: int(gaslimit),
|
||
|
V: big.Zero(),
|
||
|
R: big.Zero(),
|
||
|
S: big.Zero(),
|
||
|
}
|
||
|
|
||
|
client.EVM().SignLegacyHomesteadTransaction(&tx, key.PrivateKey)
|
||
|
// Mangle signature
|
||
|
tx.V.Int.Xor(tx.V.Int, big.NewInt(1).Int)
|
||
|
|
||
|
signed, err := tx.ToRlpSignedMsg()
|
||
|
require.NoError(t, err)
|
||
|
// Submit transaction with bad signature
|
||
|
_, err = client.EVM().EthSendRawTransaction(ctx, signed)
|
||
|
require.Error(t, err)
|
||
|
|
||
|
// Submit transaction with valid signature
|
||
|
client.EVM().SignLegacyHomesteadTransaction(&tx, key.PrivateKey)
|
||
|
|
||
|
hash := client.EVM().SubmitTransaction(ctx, &tx)
|
||
|
|
||
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, receipt)
|
||
|
require.EqualValues(t, ethAddr, receipt.From)
|
||
|
require.EqualValues(t, ethAddr2, *receipt.To)
|
||
|
require.EqualValues(t, hash, receipt.TransactionHash)
|
||
|
|
||
|
// Success.
|
||
|
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
|
||
|
|
||
|
// Validate that we sent the expected transaction.
|
||
|
ethTx, err := client.EthGetTransactionByHash(ctx, &hash)
|
||
|
require.NoError(t, err)
|
||
|
require.Nil(t, ethTx.MaxPriorityFeePerGas)
|
||
|
require.Nil(t, ethTx.MaxFeePerGas)
|
||
|
|
||
|
require.EqualValues(t, ethAddr, ethTx.From)
|
||
|
require.EqualValues(t, ethAddr2, *ethTx.To)
|
||
|
require.EqualValues(t, tx.Nonce, ethTx.Nonce)
|
||
|
require.EqualValues(t, hash, ethTx.Hash)
|
||
|
require.EqualValues(t, tx.Value, ethTx.Value)
|
||
|
require.EqualValues(t, 0, ethTx.Type)
|
||
|
require.EqualValues(t, 0, ethTx.ChainID)
|
||
|
require.EqualValues(t, ethtypes.EthBytes{}, ethTx.Input)
|
||
|
require.EqualValues(t, tx.GasLimit, ethTx.Gas)
|
||
|
require.EqualValues(t, tx.GasPrice, *ethTx.GasPrice)
|
||
|
require.EqualValues(t, tx.R, ethTx.R)
|
||
|
require.EqualValues(t, tx.S, ethTx.S)
|
||
|
require.EqualValues(t, tx.V, ethTx.V)
|
||
|
}
|
||
|
|
||
|
func TestLegacyEIP155ValueTransferValidSignatureFailsNV22(t *testing.T) {
|
||
|
blockTime := 100 * time.Millisecond
|
||
|
|
||
|
nv23Height := 10
|
||
|
// We will move to NV23 at epoch 10
|
||
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.UpgradeSchedule(stmgr.Upgrade{
|
||
|
Network: network.Version22,
|
||
|
Height: -1,
|
||
|
}, stmgr.Upgrade{
|
||
|
Network: network.Version23,
|
||
|
Height: abi.ChainEpoch(nv23Height),
|
||
|
Migration: filcns.UpgradeActorsV13,
|
||
|
}))
|
||
|
|
||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||
|
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||
|
defer cancel()
|
||
|
|
||
|
// create a new Ethereum account
|
||
|
key, ethAddr, deployer := client.EVM().NewAccount()
|
||
|
_, ethAddr2, _ := client.EVM().NewAccount()
|
||
|
|
||
|
kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000))
|
||
|
|
||
|
gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{
|
||
|
From: ðAddr,
|
||
|
To: ðAddr2,
|
||
|
Value: ethtypes.EthBigInt(big.NewInt(100)),
|
||
|
}})
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
gaslimit, err := client.EthEstimateGas(ctx, gasParams)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
legacyTx := ðtypes.EthLegacyHomesteadTxArgs{
|
||
|
Value: big.NewInt(100),
|
||
|
Nonce: 0,
|
||
|
To: ðAddr2,
|
||
|
GasPrice: types.NanoFil,
|
||
|
GasLimit: int(gaslimit),
|
||
|
V: big.Zero(),
|
||
|
R: big.Zero(),
|
||
|
S: big.Zero(),
|
||
|
}
|
||
|
tx := ethtypes.NewEthLegacy155TxArgs(legacyTx)
|
||
|
|
||
|
// TX will fail as we're still at NV22
|
||
|
client.EVM().SignLegacyEIP155Transaction(tx, key.PrivateKey, big.NewInt(build.Eip155ChainId))
|
||
|
|
||
|
signed, err := tx.ToRawTxBytesSigned()
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
_, err = client.EVM().EthSendRawTransaction(ctx, signed)
|
||
|
require.Error(t, err)
|
||
|
require.Contains(t, err.Error(), "network version should be atleast NV23 for sending legacy ETH transactions")
|
||
|
}
|
||
|
|
||
|
func TestLegacyEIP155ValueTransferValidSignature(t *testing.T) {
|
||
|
blockTime := 100 * time.Millisecond
|
||
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||
|
|
||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||
|
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||
|
defer cancel()
|
||
|
|
||
|
// create a new Ethereum account
|
||
|
key, ethAddr, deployer := client.EVM().NewAccount()
|
||
|
_, ethAddr2, _ := client.EVM().NewAccount()
|
||
|
|
||
|
kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000))
|
||
|
|
||
|
gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{
|
||
|
From: ðAddr,
|
||
|
To: ðAddr2,
|
||
|
Value: ethtypes.EthBigInt(big.NewInt(100)),
|
||
|
}})
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
gaslimit, err := client.EthEstimateGas(ctx, gasParams)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
legacyTx := ðtypes.EthLegacyHomesteadTxArgs{
|
||
|
Value: big.NewInt(100),
|
||
|
Nonce: 0,
|
||
|
To: ðAddr2,
|
||
|
GasPrice: types.NanoFil,
|
||
|
GasLimit: int(gaslimit),
|
||
|
V: big.Zero(),
|
||
|
R: big.Zero(),
|
||
|
S: big.Zero(),
|
||
|
}
|
||
|
tx := ethtypes.NewEthLegacy155TxArgs(legacyTx)
|
||
|
|
||
|
client.EVM().SignLegacyEIP155Transaction(tx, key.PrivateKey, big.NewInt(build.Eip155ChainId))
|
||
|
// Mangle signature
|
||
|
innerTx := tx.GetLegacyTx()
|
||
|
innerTx.V.Int.Xor(innerTx.V.Int, big.NewInt(1).Int)
|
||
|
|
||
|
signed, err := tx.ToRawTxBytesSigned()
|
||
|
require.NoError(t, err)
|
||
|
// Submit transaction with bad signature
|
||
|
_, err = client.EVM().EthSendRawTransaction(ctx, signed)
|
||
|
require.Error(t, err)
|
||
|
|
||
|
// Submit transaction with valid signature but incorrect chain ID
|
||
|
client.EVM().SignLegacyEIP155Transaction(tx, key.PrivateKey, big.NewInt(build.Eip155ChainId))
|
||
|
|
||
|
signed, err = tx.ToRawTxBytesSigned()
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
hash, err := client.EVM().EthSendRawTransaction(ctx, signed)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, receipt)
|
||
|
require.EqualValues(t, ethAddr, receipt.From)
|
||
|
require.EqualValues(t, ethAddr2, *receipt.To)
|
||
|
require.EqualValues(t, hash, receipt.TransactionHash)
|
||
|
|
||
|
// Success.
|
||
|
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
|
||
|
|
||
|
// Validate that we sent the expected transaction.
|
||
|
ethTx, err := client.EthGetTransactionByHash(ctx, &hash)
|
||
|
require.NoError(t, err)
|
||
|
require.Nil(t, ethTx.MaxPriorityFeePerGas)
|
||
|
require.Nil(t, ethTx.MaxFeePerGas)
|
||
|
|
||
|
innerTx = tx.GetLegacyTx()
|
||
|
require.EqualValues(t, ethAddr, ethTx.From)
|
||
|
require.EqualValues(t, ethAddr2, *ethTx.To)
|
||
|
require.EqualValues(t, innerTx.Nonce, ethTx.Nonce)
|
||
|
require.EqualValues(t, hash, ethTx.Hash)
|
||
|
require.EqualValues(t, innerTx.Value, ethTx.Value)
|
||
|
require.EqualValues(t, 0, ethTx.Type)
|
||
|
require.EqualValues(t, build.Eip155ChainId, ethTx.ChainID)
|
||
|
require.EqualValues(t, ethtypes.EthBytes{}, ethTx.Input)
|
||
|
require.EqualValues(t, innerTx.GasLimit, ethTx.Gas)
|
||
|
require.EqualValues(t, innerTx.GasPrice, *ethTx.GasPrice)
|
||
|
require.EqualValues(t, innerTx.R, ethTx.R)
|
||
|
require.EqualValues(t, innerTx.S, ethTx.S)
|
||
|
require.EqualValues(t, innerTx.V, ethTx.V)
|
||
|
}
|
||
|
|
||
|
func TestLegacyContractInvocation(t *testing.T) {
|
||
|
blockTime := 100 * time.Millisecond
|
||
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
||
|
|
||
|
ens.InterconnectAll().BeginMining(blockTime)
|
||
|
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||
|
defer cancel()
|
||
|
|
||
|
// create a new Ethereum account
|
||
|
key, ethAddr, deployer := client.EVM().NewAccount()
|
||
|
// send some funds to the f410 address
|
||
|
kit.SendFunds(ctx, t, client, deployer, types.FromFil(10))
|
||
|
|
||
|
// DEPLOY CONTRACT
|
||
|
tx, err := deployLegacyContractTx(ctx, t, client, ethAddr)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
client.EVM().SignLegacyHomesteadTransaction(tx, key.PrivateKey)
|
||
|
// Mangle signature
|
||
|
tx.V.Int.Xor(tx.V.Int, big.NewInt(1).Int)
|
||
|
|
||
|
signed, err := tx.ToRlpSignedMsg()
|
||
|
require.NoError(t, err)
|
||
|
// Submit transaction with bad signature
|
||
|
_, err = client.EVM().EthSendRawTransaction(ctx, signed)
|
||
|
require.Error(t, err)
|
||
|
|
||
|
// Submit transaction with valid signature
|
||
|
client.EVM().SignLegacyHomesteadTransaction(tx, key.PrivateKey)
|
||
|
|
||
|
hash := client.EVM().SubmitTransaction(ctx, tx)
|
||
|
|
||
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, receipt)
|
||
|
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
|
||
|
|
||
|
// Get contract address.
|
||
|
contractAddr := client.EVM().ComputeContractAddress(ethAddr, 0)
|
||
|
|
||
|
// INVOKE CONTRACT
|
||
|
|
||
|
// Params
|
||
|
// entry point for getBalance - f8b2cb4f
|
||
|
// address - ff00000000000000000000000000000000000064
|
||
|
params, err := hex.DecodeString("f8b2cb4f000000000000000000000000ff00000000000000000000000000000000000064")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{
|
||
|
From: ðAddr,
|
||
|
To: &contractAddr,
|
||
|
Data: params,
|
||
|
}})
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
gaslimit, err := client.EthEstimateGas(ctx, gasParams)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
invokeTx := ethtypes.EthLegacyHomesteadTxArgs{
|
||
|
To: &contractAddr,
|
||
|
Value: big.Zero(),
|
||
|
Nonce: 1,
|
||
|
GasPrice: big.Int(maxPriorityFeePerGas),
|
||
|
GasLimit: int(gaslimit),
|
||
|
Input: params,
|
||
|
V: big.Zero(),
|
||
|
R: big.Zero(),
|
||
|
S: big.Zero(),
|
||
|
}
|
||
|
|
||
|
client.EVM().SignLegacyHomesteadTransaction(&invokeTx, key.PrivateKey)
|
||
|
// Mangle signature
|
||
|
invokeTx.V.Int.Xor(invokeTx.V.Int, big.NewInt(1).Int)
|
||
|
|
||
|
signed, err = invokeTx.ToRlpSignedMsg()
|
||
|
require.NoError(t, err)
|
||
|
// Submit transaction with bad signature
|
||
|
_, err = client.EVM().EthSendRawTransaction(ctx, signed)
|
||
|
require.Error(t, err)
|
||
|
|
||
|
// Submit transaction with valid signature
|
||
|
client.EVM().SignLegacyHomesteadTransaction(&invokeTx, key.PrivateKey)
|
||
|
hash = client.EVM().SubmitTransaction(ctx, &invokeTx)
|
||
|
|
||
|
receipt, err = client.EVM().WaitTransaction(ctx, hash)
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, receipt)
|
||
|
|
||
|
// Success.
|
||
|
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
|
||
|
|
||
|
// Validate that we correctly computed the gas outputs.
|
||
|
mCid, err := client.EthGetMessageCidByTransactionHash(ctx, &hash)
|
||
|
require.NoError(t, err)
|
||
|
require.NotNil(t, mCid)
|
||
|
|
||
|
invokResult, err := client.StateReplay(ctx, types.EmptyTSK, *mCid)
|
||
|
require.NoError(t, err)
|
||
|
require.EqualValues(t, invokResult.GasCost.GasUsed, big.NewInt(int64(receipt.GasUsed)))
|
||
|
effectiveGasPrice := big.Div(invokResult.GasCost.TotalCost, invokResult.GasCost.GasUsed)
|
||
|
require.EqualValues(t, effectiveGasPrice, big.Int(receipt.EffectiveGasPrice))
|
||
|
}
|
||
|
|
||
|
func deployLegacyContractTx(ctx context.Context, t *testing.T, client *kit.TestFullNode, ethAddr ethtypes.EthAddress) (*ethtypes.EthLegacyHomesteadTxArgs, error) {
|
||
|
// install contract
|
||
|
contractHex, err := os.ReadFile("./contracts/SimpleCoin.hex")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
contract, err := hex.DecodeString(string(contractHex))
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{Tx: ethtypes.EthCall{
|
||
|
From: ðAddr,
|
||
|
Data: contract,
|
||
|
}})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
gaslimit, err := client.EthEstimateGas(ctx, gasParams)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// now deploy a contract from the embryo, and validate it went well
|
||
|
return ðtypes.EthLegacyHomesteadTxArgs{
|
||
|
Value: big.Zero(),
|
||
|
Nonce: 0,
|
||
|
GasPrice: big.Int(maxPriorityFeePerGas),
|
||
|
GasLimit: int(gaslimit),
|
||
|
Input: contract,
|
||
|
V: big.Zero(),
|
||
|
R: big.Zero(),
|
||
|
S: big.Zero(),
|
||
|
}, nil
|
||
|
}
|