mirror of
https://github.com/filecoin-project/lotus.git
synced 2025-08-23 16:55:22 +08:00
438 lines
14 KiB
Go
438 lines
14 KiB
Go
package itests
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/filecoin-project/go-address"
|
|
"github.com/filecoin-project/go-jsonrpc"
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
"github.com/filecoin-project/go-state-types/big"
|
|
"github.com/filecoin-project/go-state-types/builtin"
|
|
inittypes "github.com/filecoin-project/go-state-types/builtin/v8/init"
|
|
"github.com/filecoin-project/go-state-types/exitcode"
|
|
|
|
"github.com/filecoin-project/lotus/build/buildconstants"
|
|
"github.com/filecoin-project/lotus/chain/actors/policy"
|
|
"github.com/filecoin-project/lotus/chain/types"
|
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
|
"github.com/filecoin-project/lotus/chain/wallet/key"
|
|
"github.com/filecoin-project/lotus/itests/kit"
|
|
"github.com/filecoin-project/lotus/lib/result"
|
|
)
|
|
|
|
func TestETHGetBlockByHashWithCache(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()
|
|
|
|
// install contract
|
|
contractHex, err := os.ReadFile("./contracts/SimpleCoin.hex")
|
|
require.NoError(t, err)
|
|
|
|
contract, err := hex.DecodeString(string(contractHex))
|
|
require.NoError(t, err)
|
|
|
|
// create a new Ethereum account
|
|
key, ethAddr, deployer := client.EVM().NewAccount()
|
|
_, ethAddr2, _ := client.EVM().NewAccount()
|
|
|
|
kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000))
|
|
|
|
blkParam := ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")
|
|
gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{
|
|
Tx: ethtypes.EthCall{
|
|
From: ðAddr,
|
|
Data: contract,
|
|
},
|
|
BlkParam: &blkParam,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
gaslimit, err := client.EthEstimateGas(ctx, gasParams)
|
|
require.NoError(t, err)
|
|
|
|
maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
|
|
require.NoError(t, err)
|
|
|
|
tx := ethtypes.Eth1559TxArgs{
|
|
ChainID: buildconstants.Eip155ChainId,
|
|
Value: big.NewInt(100),
|
|
Nonce: 0,
|
|
To: ðAddr2,
|
|
MaxFeePerGas: types.NanoFil,
|
|
MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas),
|
|
GasLimit: int(gaslimit),
|
|
V: big.Zero(),
|
|
R: big.Zero(),
|
|
S: big.Zero(),
|
|
}
|
|
|
|
// Submit transaction with valid signature
|
|
client.EVM().SignTransaction(&tx, key.PrivateKey)
|
|
hash := client.EVM().SubmitTransaction(ctx, &tx)
|
|
|
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, receipt)
|
|
|
|
blkHash := receipt.BlockHash
|
|
|
|
// call EthGetBlockByHash without tx info
|
|
blk1, err := client.EthGetBlockByHash(ctx, blkHash, false)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, blkHash, blk1.Hash)
|
|
|
|
// call again to exercise the cache
|
|
blk2, err := client.EthGetBlockByHash(ctx, blkHash, false)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, blkHash, blk2.Hash)
|
|
|
|
// call EthGetBlockByHash with tx info
|
|
blk3, err := client.EthGetBlockByHash(ctx, blkHash, true)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, blkHash, blk3.Hash)
|
|
|
|
// call again to exercise the cache
|
|
blk4, err := client.EthGetBlockByHash(ctx, blkHash, true)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, blkHash, blk4.Hash)
|
|
}
|
|
|
|
func TestETHGetBlockByHashWithoutCache(t *testing.T) {
|
|
blockTime := 100 * time.Millisecond
|
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableETHBlockCache())
|
|
|
|
ens.InterconnectAll().BeginMining(blockTime)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
|
|
// install contract
|
|
contractHex, err := os.ReadFile("./contracts/SimpleCoin.hex")
|
|
require.NoError(t, err)
|
|
|
|
contract, err := hex.DecodeString(string(contractHex))
|
|
require.NoError(t, err)
|
|
|
|
// create a new Ethereum account
|
|
key, ethAddr, deployer := client.EVM().NewAccount()
|
|
_, ethAddr2, _ := client.EVM().NewAccount()
|
|
|
|
kit.SendFunds(ctx, t, client, deployer, types.FromFil(1000))
|
|
|
|
blkParam := ethtypes.NewEthBlockNumberOrHashFromPredefined("latest")
|
|
gasParams, err := json.Marshal(ethtypes.EthEstimateGasParams{
|
|
Tx: ethtypes.EthCall{
|
|
From: ðAddr,
|
|
Data: contract,
|
|
},
|
|
BlkParam: &blkParam,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
gaslimit, err := client.EthEstimateGas(ctx, gasParams)
|
|
require.NoError(t, err)
|
|
|
|
maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx)
|
|
require.NoError(t, err)
|
|
|
|
tx := ethtypes.Eth1559TxArgs{
|
|
ChainID: buildconstants.Eip155ChainId,
|
|
Value: big.NewInt(100),
|
|
Nonce: 0,
|
|
To: ðAddr2,
|
|
MaxFeePerGas: types.NanoFil,
|
|
MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas),
|
|
GasLimit: int(gaslimit),
|
|
V: big.Zero(),
|
|
R: big.Zero(),
|
|
S: big.Zero(),
|
|
}
|
|
|
|
// Submit transaction with valid signature
|
|
client.EVM().SignTransaction(&tx, key.PrivateKey)
|
|
hash := client.EVM().SubmitTransaction(ctx, &tx)
|
|
|
|
receipt, err := client.EVM().WaitTransaction(ctx, hash)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, receipt)
|
|
|
|
blkHash := receipt.BlockHash
|
|
|
|
// call EthGetBlockByHash without tx info
|
|
blk1, err := client.EthGetBlockByHash(ctx, blkHash, false)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, blkHash, blk1.Hash)
|
|
|
|
// call again
|
|
blk2, err := client.EthGetBlockByHash(ctx, blkHash, false)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, blkHash, blk2.Hash)
|
|
}
|
|
|
|
func TestEthAddressToFilecoinAddress(t *testing.T) {
|
|
// Disable EthRPC to confirm that this method does NOT need the EthEnableRPC config set to true
|
|
client, _, _ := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC())
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
|
|
secpKey, err := key.GenerateKey(types.KTDelegated)
|
|
require.NoError(t, err)
|
|
|
|
filecoinKeyAddr, err := client.WalletImport(ctx, &secpKey.KeyInfo)
|
|
require.NoError(t, err)
|
|
|
|
ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(filecoinKeyAddr)
|
|
require.NoError(t, err)
|
|
|
|
apiFilAddr, err := client.EthAddressToFilecoinAddress(ctx, ethAddr)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, filecoinKeyAddr, apiFilAddr)
|
|
|
|
filecoinIdArr := builtin.StorageMarketActorAddr
|
|
ethAddr, err = ethtypes.EthAddressFromFilecoinAddress(filecoinIdArr)
|
|
require.NoError(t, err)
|
|
|
|
apiFilAddr, err = client.EthAddressToFilecoinAddress(ctx, ethAddr)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, filecoinIdArr, apiFilAddr)
|
|
|
|
}
|
|
|
|
func TestFilecoinAddressToEthAddress(t *testing.T) {
|
|
require := require.New(t)
|
|
blockTime := 100 * time.Millisecond
|
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC())
|
|
ens.InterconnectAll().BeginMining(blockTime)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
|
|
// Test for f4 address
|
|
filecoinDelegatedAddr, err := client.WalletNew(ctx, types.KTDelegated)
|
|
require.NoError(err)
|
|
ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(filecoinDelegatedAddr)
|
|
require.NoError(err)
|
|
|
|
apiEthAddr, err := client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{filecoinDelegatedAddr}),
|
|
).Assert(require.NoError))
|
|
|
|
require.NoError(err)
|
|
require.Equal(ethAddr, apiEthAddr)
|
|
fAddr, err := client.EthAddressToFilecoinAddress(ctx, ethAddr)
|
|
require.NoError(err)
|
|
require.Equal(filecoinDelegatedAddr, fAddr)
|
|
|
|
// test for f0 address
|
|
filecoinIdArr := builtin.StorageMarketActorAddr
|
|
ethAddr, err = ethtypes.EthAddressFromFilecoinAddress(filecoinIdArr)
|
|
require.NoError(err)
|
|
|
|
apiEthAddr, err = client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{filecoinIdArr}),
|
|
).Assert(require.NoError))
|
|
require.NoError(err)
|
|
|
|
require.Equal(ethAddr, apiEthAddr)
|
|
fAddr, err = client.EthAddressToFilecoinAddress(ctx, ethAddr)
|
|
require.NoError(err)
|
|
require.Equal(filecoinIdArr, fAddr)
|
|
|
|
// test for f1 address that does not yet exist on chain -> fails
|
|
filecoinSecpAddr, err := client.WalletNew(ctx, types.KTSecp256k1)
|
|
require.NoError(err)
|
|
|
|
_, err = client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{filecoinSecpAddr, "latest"}),
|
|
).Assert(require.NoError))
|
|
require.Error(err)
|
|
require.ErrorContains(err, "actor not found")
|
|
|
|
// test for f1 address that exists on chain by sending funds to the above f1 Actor -> works
|
|
kit.SendFunds(ctx, t, client, filecoinSecpAddr, abi.NewTokenAmount(1))
|
|
apiEthAddr, err = client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{filecoinSecpAddr, "latest"}),
|
|
).Assert(require.NoError))
|
|
require.NoError(err)
|
|
|
|
idAddr, err := client.StateLookupID(ctx, filecoinSecpAddr, types.EmptyTSK)
|
|
require.NoError(err)
|
|
ethAddr, err = ethtypes.EthAddressFromFilecoinAddress(idAddr)
|
|
require.NoError(err)
|
|
require.Equal(ethAddr, apiEthAddr)
|
|
fAddr, err = client.EthAddressToFilecoinAddress(ctx, ethAddr)
|
|
require.NoError(err)
|
|
require.Equal(idAddr, fAddr)
|
|
|
|
// test for f2 address that exists on chain -> works
|
|
signer := client.DefaultKey.Address
|
|
|
|
// Create the multisig
|
|
cp, err := client.MsigCreate(ctx, 1, []address.Address{signer}, 0, types.NewInt(1), signer, big.Zero())
|
|
require.NoError(err, "failed to create multisig (MsigCreate)")
|
|
|
|
cm, err := client.MpoolPushMessage(ctx, &cp.Message, nil)
|
|
require.NoError(err, "failed to create multisig (MpooPushMessage)")
|
|
|
|
ml, err := client.StateWaitMsg(ctx, cm.Cid(), 5, 100, false)
|
|
require.NoError(err, "failed to create multisig (StateWaitMsg)")
|
|
require.Equal(ml.Receipt.ExitCode, exitcode.Ok)
|
|
|
|
var execreturn inittypes.ExecReturn
|
|
err = execreturn.UnmarshalCBOR(bytes.NewReader(ml.Receipt.Return))
|
|
require.NoError(err, "failed to decode multisig create return")
|
|
|
|
multisigAddress := execreturn.RobustAddress
|
|
apiEthAddr, err = client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{multisigAddress, "latest"}),
|
|
).Assert(require.NoError))
|
|
require.NoError(err)
|
|
|
|
idAddr, err = client.StateLookupID(ctx, multisigAddress, types.EmptyTSK)
|
|
require.NoError(err)
|
|
|
|
ethAddr, err = ethtypes.EthAddressFromFilecoinAddress(idAddr)
|
|
require.NoError(err)
|
|
require.Equal(ethAddr, apiEthAddr)
|
|
}
|
|
|
|
func TestFilecoinAddressToEthAddressFinalised(t *testing.T) {
|
|
require := require.New(t)
|
|
blockTime := 5 * time.Millisecond
|
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC())
|
|
ens.InterconnectAll().BeginMining(blockTime)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
|
|
// wait till we have enough epochs to perform a finality lookback
|
|
_ = client.WaitTillChain(ctx, kit.HeightAtLeast(policy.ChainFinality))
|
|
|
|
filecoinSecpAddr, err := client.WalletNew(ctx, types.KTSecp256k1)
|
|
require.NoError(err)
|
|
kit.SendFunds(ctx, t, client, filecoinSecpAddr, abi.NewTokenAmount(1))
|
|
// works for latest
|
|
_, err = client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{filecoinSecpAddr, "latest"}),
|
|
).Assert(require.NoError))
|
|
require.NoError(err)
|
|
|
|
// but fails for finalised
|
|
_, err = client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{filecoinSecpAddr, "finalized"}),
|
|
).Assert(require.NoError))
|
|
require.Error(err)
|
|
require.Contains(err.Error(), "sufficient epochs have passed")
|
|
|
|
// wait for enough epochs to pass for finalised to work
|
|
ts, err := client.ChainHead(ctx)
|
|
require.NoError(err)
|
|
_ = client.WaitTillChain(ctx, kit.HeightAtLeast(policy.ChainFinality+ts.Height()+5))
|
|
// finalized should now work
|
|
apiEthAddr, err := client.FilecoinAddressToEthAddress(ctx, result.Wrap[jsonrpc.RawParams](
|
|
json.Marshal([]interface{}{filecoinSecpAddr, "finalized"}),
|
|
).Assert(require.NoError))
|
|
require.NoError(err)
|
|
|
|
idAddr, err := client.StateLookupID(ctx, filecoinSecpAddr, types.EmptyTSK)
|
|
require.NoError(err)
|
|
ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(idAddr)
|
|
require.NoError(err)
|
|
require.Equal(ethAddr, apiEthAddr)
|
|
}
|
|
|
|
func TestEthGetGenesis(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()
|
|
|
|
ethBlk, err := client.EVM().EthGetBlockByNumber(ctx, "0x0", true)
|
|
require.NoError(t, err)
|
|
|
|
genesis, err := client.ChainGetGenesis(ctx)
|
|
require.NoError(t, err)
|
|
|
|
genesisCid, err := genesis.Key().Cid()
|
|
require.NoError(t, err)
|
|
|
|
genesisHash, err := ethtypes.EthHashFromCid(genesisCid)
|
|
require.NoError(t, err)
|
|
require.Equal(t, ethBlk.Hash, genesisHash)
|
|
}
|
|
|
|
func TestNetVersion(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()
|
|
|
|
version, err := client.NetVersion(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, strconv.Itoa(buildconstants.Eip155ChainId), version)
|
|
}
|
|
|
|
func TestEthBlockNumberAliases(t *testing.T) {
|
|
blockTime := 2 * time.Millisecond
|
|
kit.QuietMiningLogs()
|
|
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
|
|
ens.InterconnectAll().BeginMining(blockTime)
|
|
ens.Start()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
|
defer cancel()
|
|
|
|
client.WaitTillChain(ctx, kit.HeightAtLeast(policy.ChainFinality+100))
|
|
|
|
for _, tc := range []struct {
|
|
param string
|
|
expectedLag abi.ChainEpoch
|
|
}{
|
|
{"latest", 1}, // head - 1
|
|
{"safe", 30 + 1}, // "latest" - 30
|
|
{"finalized", policy.ChainFinality + 1}, // "latest" - 900
|
|
} {
|
|
t.Run(tc.param, func(t *testing.T) {
|
|
head, err := client.ChainHead(ctx)
|
|
require.NoError(t, err)
|
|
var blk ethtypes.EthBlock
|
|
for { // get a block while retaining a stable "head" reference
|
|
blk, err = client.EVM().EthGetBlockByNumber(ctx, tc.param, true)
|
|
require.NoError(t, err)
|
|
afterHead, err := client.ChainHead(ctx)
|
|
require.NoError(t, err)
|
|
if afterHead.Height() == head.Height() {
|
|
break
|
|
}
|
|
// else: whoops, we had a chain increment between getting head and getting "latest" so
|
|
// we won't be able to use head as a stable reference for comparison
|
|
head = afterHead
|
|
}
|
|
ts, err := client.ChainGetTipSetByHeight(ctx, head.Height()-tc.expectedLag, head.Key())
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, ts.Height(), blk.Number)
|
|
})
|
|
}
|
|
}
|