2023-01-19 18:25:23 +00:00
|
|
|
package itests
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2023-03-09 17:14:23 +00:00
|
|
|
"strings"
|
2023-01-19 18:25:23 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"github.com/filecoin-project/go-state-types/abi"
|
|
|
|
|
2024-03-21 19:49:55 +00:00
|
|
|
"github.com/filecoin-project/lotus/build"
|
|
|
|
"github.com/filecoin-project/lotus/chain/types/ethtypes"
|
2023-01-19 18:25:23 +00:00
|
|
|
"github.com/filecoin-project/lotus/itests/kit"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TestEthBlockHashesCorrect_MultiBlockTipset validates that blocks retrieved through
|
|
|
|
// EthGetBlockByNumber are identical to blocks retrieved through
|
|
|
|
// EthGetBlockByHash, when using the block hash returned by the former.
|
|
|
|
//
|
|
|
|
// Specifically, it checks the system behaves correctly with multiblock tipsets.
|
|
|
|
//
|
|
|
|
// Catches regressions around https://github.com/filecoin-project/lotus/issues/10061.
|
|
|
|
func TestEthBlockHashesCorrect_MultiBlockTipset(t *testing.T) {
|
|
|
|
// miner is connected to the first node, and we want to observe the chain
|
|
|
|
// from the second node.
|
2024-04-04 02:10:31 +00:00
|
|
|
blocktime := 100 * time.Millisecond
|
2023-01-19 18:25:23 +00:00
|
|
|
n1, m1, m2, ens := kit.EnsembleOneTwo(t,
|
|
|
|
kit.MockProofs(),
|
|
|
|
kit.ThroughRPC(),
|
|
|
|
)
|
|
|
|
ens.InterconnectAll().BeginMining(blocktime)
|
|
|
|
|
2024-04-04 02:10:31 +00:00
|
|
|
{
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
|
|
n1.WaitTillChain(ctx, kit.HeightAtLeast(abi.ChainEpoch(5)))
|
|
|
|
cancel()
|
|
|
|
}
|
2023-01-19 18:25:23 +00:00
|
|
|
|
|
|
|
var n2 kit.TestFullNode
|
|
|
|
ens.FullNode(&n2, kit.ThroughRPC()).Start().Connect(n2, n1)
|
|
|
|
|
2024-04-04 02:10:31 +00:00
|
|
|
{
|
|
|
|
// find the first tipset where all miners mined a block.
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
|
|
|
n2.WaitTillChain(ctx, kit.BlocksMinedByAll(m1.ActorAddr, m2.ActorAddr))
|
|
|
|
cancel()
|
|
|
|
}
|
2023-01-19 18:25:23 +00:00
|
|
|
|
|
|
|
head, err := n2.ChainHead(context.Background())
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2024-04-04 02:10:31 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
|
2023-01-19 18:25:23 +00:00
|
|
|
// let the chain run a little bit longer to minimise the chance of reorgs
|
2024-04-04 02:10:31 +00:00
|
|
|
n2.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+10))
|
2023-01-19 18:25:23 +00:00
|
|
|
|
2024-03-21 19:49:55 +00:00
|
|
|
tsk := head.Key()
|
2023-01-19 18:25:23 +00:00
|
|
|
for i := 1; i <= int(head.Height()); i++ {
|
|
|
|
hex := fmt.Sprintf("0x%x", i)
|
|
|
|
|
2024-03-21 19:49:55 +00:00
|
|
|
ts, err := n2.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(i), tsk)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2023-01-19 18:25:23 +00:00
|
|
|
ethBlockA, err := n2.EthGetBlockByNumber(ctx, hex, true)
|
2023-03-09 17:14:23 +00:00
|
|
|
// Cannot use static ErrFullRound error for comparison since it gets reserialized as a JSON RPC error.
|
|
|
|
if err != nil && strings.Contains(err.Error(), "null round") {
|
2024-03-21 19:49:55 +00:00
|
|
|
require.Less(t, ts.Height(), abi.ChainEpoch(i), "did not expect a tipset at epoch %d", i)
|
2023-03-09 17:14:23 +00:00
|
|
|
continue
|
|
|
|
}
|
2023-01-19 18:25:23 +00:00
|
|
|
require.NoError(t, err)
|
2024-03-21 19:49:55 +00:00
|
|
|
require.Equal(t, ts.Height(), abi.ChainEpoch(i), "expected a tipset at epoch %i", i)
|
2023-01-19 18:25:23 +00:00
|
|
|
|
|
|
|
ethBlockB, err := n2.EthGetBlockByHash(ctx, ethBlockA.Hash, true)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.Equal(t, ethBlockA, ethBlockB)
|
2024-03-21 19:49:55 +00:00
|
|
|
|
|
|
|
numBlocks := len(ts.Blocks())
|
|
|
|
expGasLimit := ethtypes.EthUint64(int64(numBlocks) * build.BlockGasLimit)
|
|
|
|
require.Equal(t, expGasLimit, ethBlockB.GasLimit)
|
2023-01-19 18:25:23 +00:00
|
|
|
}
|
|
|
|
}
|