diff --git a/itests/contracts/mcopy/MCOPYTest.hex b/itests/contracts/mcopy/MCOPYTest.hex new file mode 100644 index 00000000000..f2e667e9343 --- /dev/null +++ b/itests/contracts/mcopy/MCOPYTest.hex @@ -0,0 +1 @@ +6080604052348015600e575f80fd5b506103148061001c5f395ff3fe608060405234801561000f575f80fd5b5060043610610029575f3560e01c8063733580551461002d575b5f80fd5b61004760048036038101906100429190610217565b61005d565b60405161005491906102be565b60405180910390f35b60605f825167ffffffffffffffff81111561007b5761007a6100f3565b5b6040519080825280601f01601f1916602001820160405280156100ad5781602001600182028036833780820191505090505b509050825160208401602083018282825e50505080915050919050565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610129826100e3565b810181811067ffffffffffffffff82111715610148576101476100f3565b5b80604052505050565b5f61015a6100ca565b90506101668282610120565b919050565b5f67ffffffffffffffff821115610185576101846100f3565b5b61018e826100e3565b9050602081019050919050565b828183375f83830152505050565b5f6101bb6101b68461016b565b610151565b9050828152602081018484840111156101d7576101d66100df565b5b6101e284828561019b565b509392505050565b5f82601f8301126101fe576101fd6100db565b5b813561020e8482602086016101a9565b91505092915050565b5f6020828403121561022c5761022b6100d3565b5b5f82013567ffffffffffffffff811115610249576102486100d7565b5b610255848285016101ea565b91505092915050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6102908261025e565b61029a8185610268565b93506102aa818560208601610278565b6102b3816100e3565b840191505092915050565b5f6020820190508181035f8301526102d68184610286565b90509291505056fea2646970667358221220636e7de4492dd5c1e8857edc7182c645466bf34d0a2a0cea445a7de5360a9fe364736f6c634300081a0033 diff --git a/itests/contracts/mcopy/MCOPYTest.sol b/itests/contracts/mcopy/MCOPYTest.sol new file mode 100644 index 00000000000..21e0ed39746 --- /dev/null +++ b/itests/contracts/mcopy/MCOPYTest.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract MCOPYTest { + function optimizedCopy(bytes memory data) public pure returns (bytes memory) { + bytes memory result = new bytes(data.length); + assembly { + let length := mload(data) // Get the length of the input data + let source := add(data, 0x20) // Point to the start of the data (skip the length) + let destination := add(result, 0x20) // Point to the start of the result memory (skip the length) + + // Use MCOPY opcode directly for memory copying + // destination: destination memory pointer + // source: source memory pointer + // length: number of bytes to copy + mcopy(destination, source, length) + } + return result; + } + +} diff --git a/itests/fevm_test.go b/itests/fevm_test.go index 6b221804152..19f9af0178c 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -15,14 +15,18 @@ import ( "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/manifest" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build/buildconstants" + "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" @@ -1310,6 +1314,54 @@ func TestEthGetTransactionCount(t *testing.T) { require.Zero(t, contractNonceAfterDestroy) } +func TestMcopy(t *testing.T) { + // MCOPY introduced in nv24, start the test on nv23 to check the error, then upgrade at epoch 100 + // and check that an MCOPY contract can be deployed and run. + nv24epoch := abi.ChainEpoch(100) + upgradeSchedule := kit.UpgradeSchedule( + stmgr.Upgrade{ + Network: network.Version23, + Height: -1, + }, + stmgr.Upgrade{ + Network: network.Version24, + Height: nv24epoch, + Migration: filcns.UpgradeActorsV15, + }, + ) + + ctx, cancel, client := kit.SetupFEVMTest(t, upgradeSchedule) + defer cancel() + + // try to deploy the contract before the upgrade, expect an error somewhere' in deploy or in call, + // if the error is in deploy we may need to implement DeployContractFromFilename here where we can + // assert an error + + // 0000000000000000000000000000000000000000000000000000000000000020: The offset for the bytes argument (32 bytes). + // 0000000000000000000000000000000000000000000000000000000000000008: The length of the bytes data (8 bytes for "testdata"). + // 7465737464617461000000000000000000000000000000000000000000000000: The hexadecimal representation of "testdata", padded to 32 bytes. + hexString := "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000087465737464617461000000000000000000000000000000000000000000000000" + + // Decode the hex string into a byte slice + inputArgument, err := hex.DecodeString(hexString) + require.NoError(t, err) + + filenameActor := "contracts/mcopy/MCOPYTest.hex" + fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) + _, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "optimizedCopy(bytes)", inputArgument) + // We expect an error here due to MCOPY not being available in this network version + require.ErrorContains(t, err, "undefined instruction (35)") + + // wait for the upgrade + client.WaitTillChain(ctx, kit.HeightAtLeast(nv24epoch+5)) + + // should be able to deploy and call the contract now + fromAddr, contractAddr = client.EVM().DeployContractFromFilename(ctx, filenameActor) + result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "optimizedCopy(bytes)", inputArgument) + require.NoError(t, err) + require.Equal(t, inputArgument, result) +} + func TestEthGetBlockByNumber(t *testing.T) { blockTime := 100 * time.Millisecond client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) diff --git a/itests/kit/evm.go b/itests/kit/evm.go index e1848b245d6..14f37ecf3a8 100644 --- a/itests/kit/evm.go +++ b/itests/kit/evm.go @@ -410,7 +410,7 @@ func removeLeadingZeros(data []byte) []byte { return data[firstNonZeroIndex:] } -func SetupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *TestFullNode) { +func SetupFEVMTest(t *testing.T, opts ...interface{}) (context.Context, context.CancelFunc, *TestFullNode) { // make all logs extra quiet for fevm tests lvl, err := logging.LevelFromString("error") if err != nil { @@ -419,7 +419,9 @@ func SetupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *TestFull logging.SetAllLoggers(lvl) blockTime := 100 * time.Millisecond - client, _, ens := EnsembleMinimal(t, MockProofs(), ThroughRPC()) + + opts = append([]interface{}{MockProofs(), ThroughRPC()}, opts...) + client, _, ens := EnsembleMinimal(t, opts...) ens.InterconnectAll().BeginMining(blockTime) ctx, cancel := context.WithTimeout(context.Background(), time.Minute)