Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions util/testutil/fvm_call_actor_by_address_bytecode.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0x60806040525f5f5f5f5f5f5f3681019061001991906105c8565b9550955095509550955095506002815111610069576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161006090610709565b60405180910390fd5b600460f81b815f8151811061008157610080610727565b5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916146100ee576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100e5906107c4565b60405180910390fd5b600a60f81b8160018151811061010757610106610727565b5b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614610174576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161016b90610852565b60405180910390fd5b60168151146101b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101af906108e0565b60405180910390fd5b5f67ffffffffffffffff168667ffffffffffffffff161461020e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102059061096e565b60405180910390fd5b5f67ffffffffffffffff168467ffffffffffffffff1614610264576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025b906109fc565b60405180910390fd5b5f67ffffffffffffffff168367ffffffffffffffff16146102ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102b190610a8a565b60405180910390fd5b5f8251146102fd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102f490610b18565b60405180910390fd5b5f816016015190505f8173ffffffffffffffffffffffffffffffffffffffff168760405161032a90610b63565b5f6040518083038185875af1925050503d805f8114610364576040519150601f19603f3d011682016040523d82523d5f602084013e610369565b606091505b50509050606081156103ae575f5f60405180602001604052805f81525060405160200161039893929190610bfe565b6040516020818303038152906040529050610403565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb5f60405180602001604052805f8152506040516020016103f193929190610bfe565b60405160208183030381529060405290505b805160208201f35b5f604051905090565b5f5ffd5b5f5ffd5b5f67ffffffffffffffff82169050919050565b6104388161041c565b8114610442575f5ffd5b50565b5f813590506104538161042f565b92915050565b5f819050919050565b61046b81610459565b8114610475575f5ffd5b50565b5f8135905061048681610462565b92915050565b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6104da82610494565b810181811067ffffffffffffffff821117156104f9576104f86104a4565b5b80604052505050565b5f61050b61040b565b905061051782826104d1565b919050565b5f67ffffffffffffffff821115610536576105356104a4565b5b61053f82610494565b9050602081019050919050565b828183375f83830152505050565b5f61056c6105678461051c565b610502565b90508281526020810184848401111561058857610587610490565b5b61059384828561054c565b509392505050565b5f82601f8301126105af576105ae61048c565b5b81356105bf84826020860161055a565b91505092915050565b5f5f5f5f5f5f60c087890312156105e2576105e1610414565b5b5f6105ef89828a01610445565b965050602061060089828a01610478565b955050604061061189828a01610445565b945050606061062289828a01610445565b935050608087013567ffffffffffffffff81111561064357610642610418565b5b61064f89828a0161059b565b92505060a087013567ffffffffffffffff8111156106705761066f610418565b5b61067c89828a0161059b565b9150509295509295509295565b5f82825260208201905092915050565b7f46564d43616c6c4163746f724279416464726573733a20496e76616c696420735f8201527f686f727420616464726573730000000000000000000000000000000000000000602082015250565b5f6106f3602c83610689565b91506106fe82610699565b604082019050919050565b5f6020820190508181035f830152610720816106e7565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f46564d43616c6c4163746f724279416464726573733a204f6e6c7920663420615f8201527f646472657373657320737570706f727465640000000000000000000000000000602082015250565b5f6107ae603283610689565b91506107b982610754565b604082019050919050565b5f6020820190508181035f8301526107db816107a2565b9050919050565b7f46564d43616c6c4163746f724279416464726573733a204f6e6c7920663431305f8201527f2061646472657373657320737570706f72746564000000000000000000000000602082015250565b5f61083c603483610689565b9150610847826107e2565b604082019050919050565b5f6020820190508181035f83015261086981610830565b9050919050565b7f46564d43616c6c4163746f724279416464726573733a20496e76616c696420665f8201527f3431302061646472657373206c656e6774680000000000000000000000000000602082015250565b5f6108ca603283610689565b91506108d582610870565b604082019050919050565b5f6020820190508181035f8301526108f7816108be565b9050919050565b7f46564d43616c6c4163746f724279416464726573733a204f6e6c79206d6574685f8201527f6f642030202873656e642920737570706f727465640000000000000000000000602082015250565b5f610958603583610689565b9150610963826108fe565b604082019050919050565b5f6020820190508181035f8301526109858161094c565b9050919050565b7f46564d43616c6c4163746f724279416464726573733a204f6e6c79206e6f6e2d5f8201527f726561646f6e6c792063616c6c7320737570706f727465640000000000000000602082015250565b5f6109e6603883610689565b91506109f18261098c565b604082019050919050565b5f6020820190508181035f830152610a13816109da565b9050919050565b7f46564d43616c6c4163746f724279416464726573733a204f6e6c79206e6f2d635f8201527f6f6465632063616c6c7320737570706f72746564000000000000000000000000602082015250565b5f610a74603483610689565b9150610a7f82610a1a565b604082019050919050565b5f6020820190508181035f830152610aa181610a68565b9050919050565b7f46564d43616c6c4163746f724279416464726573733a204e6f20706172616d735f8201527f2065787065637465640000000000000000000000000000000000000000000000602082015250565b5f610b02602983610689565b9150610b0d82610aa8565b604082019050919050565b5f6020820190508181035f830152610b2f81610af6565b9050919050565b5f81905092915050565b50565b5f610b4e5f83610b36565b9150610b5982610b40565b5f82019050919050565b5f610b6d82610b43565b9150819050919050565b5f819050919050565b610b8981610b77565b82525050565b610b988161041c565b82525050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f610bd082610b9e565b610bda8185610ba8565b9350610bea818560208601610bb8565b610bf381610494565b840191505092915050565b5f606082019050610c115f830186610b80565b610c1e6020830185610b8f565b8181036040830152610c308184610bc6565b905094935050505056fea2646970667358221220f1098bf2d0d55cf798988475faf22c2d74b959957bae4fca1e77ac1a7c9b6a0264736f6c63430008210033
1 change: 1 addition & 0 deletions util/testutil/fvm_call_actor_by_id_bytecode.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0x60806040525f5f5f5f5f5f5f368101906100199190610498565b955095509550955095509550606367ffffffffffffffff168167ffffffffffffffff161461007c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610073906105bd565b60405180910390fd5b5f67ffffffffffffffff168667ffffffffffffffff16146100d2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c99061064b565b60405180910390fd5b5f67ffffffffffffffff168467ffffffffffffffff1614610128576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161011f906106d9565b60405180910390fd5b5f67ffffffffffffffff168367ffffffffffffffff161461017e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161017590610767565b60405180910390fd5b5f8251146101c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101b8906107f5565b60405180910390fd5b5f73ff0000000000000000000000000000000000006373ffffffffffffffffffffffffffffffffffffffff16866040516101fa90610840565b5f6040518083038185875af1925050503d805f8114610234576040519150601f19603f3d011682016040523d82523d5f602084013e610239565b606091505b505090506060811561027e575f5f60405180602001604052805f815250604051602001610268939291906108db565b60405160208183030381529060405290506102d3565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb5f60405180602001604052805f8152506040516020016102c1939291906108db565b60405160208183030381529060405290505b805160208201f35b5f604051905090565b5f5ffd5b5f5ffd5b5f67ffffffffffffffff82169050919050565b610308816102ec565b8114610312575f5ffd5b50565b5f81359050610323816102ff565b92915050565b5f819050919050565b61033b81610329565b8114610345575f5ffd5b50565b5f8135905061035681610332565b92915050565b5f5ffd5b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6103aa82610364565b810181811067ffffffffffffffff821117156103c9576103c8610374565b5b80604052505050565b5f6103db6102db565b90506103e782826103a1565b919050565b5f67ffffffffffffffff82111561040657610405610374565b5b61040f82610364565b9050602081019050919050565b828183375f83830152505050565b5f61043c610437846103ec565b6103d2565b90508281526020810184848401111561045857610457610360565b5b61046384828561041c565b509392505050565b5f82601f83011261047f5761047e61035c565b5b813561048f84826020860161042a565b91505092915050565b5f5f5f5f5f5f60c087890312156104b2576104b16102e4565b5b5f6104bf89828a01610315565b96505060206104d089828a01610348565b95505060406104e189828a01610315565b94505060606104f289828a01610315565b935050608087013567ffffffffffffffff811115610513576105126102e8565b5b61051f89828a0161046b565b92505060a061053089828a01610315565b9150509295509295509295565b5f82825260208201905092915050565b7f46564d43616c6c4163746f72427949643a204f6e6c79206275726e206163746f5f8201527f72202839392920737570706f7274656400000000000000000000000000000000602082015250565b5f6105a760308361053d565b91506105b28261054d565b604082019050919050565b5f6020820190508181035f8301526105d48161059b565b9050919050565b7f46564d43616c6c4163746f72427949643a204f6e6c79206d6574686f642030205f8201527f2873656e642920737570706f7274656400000000000000000000000000000000602082015250565b5f61063560308361053d565b9150610640826105db565b604082019050919050565b5f6020820190508181035f83015261066281610629565b9050919050565b7f46564d43616c6c4163746f72427949643a204f6e6c79206e6f6e2d726561646f5f8201527f6e6c792063616c6c7320737570706f7274656400000000000000000000000000602082015250565b5f6106c360338361053d565b91506106ce82610669565b604082019050919050565b5f6020820190508181035f8301526106f0816106b7565b9050919050565b7f46564d43616c6c4163746f72427949643a204f6e6c79206e6f2d636f646563205f8201527f63616c6c7320737570706f727465640000000000000000000000000000000000602082015250565b5f610751602f8361053d565b915061075c826106f7565b604082019050919050565b5f6020820190508181035f83015261077e81610745565b9050919050565b7f46564d43616c6c4163746f72427949643a204e6f20706172616d7320657870655f8201527f6374656400000000000000000000000000000000000000000000000000000000602082015250565b5f6107df60248361053d565b91506107ea82610785565b604082019050919050565b5f6020820190508181035f83015261080c816107d3565b9050919050565b5f81905092915050565b50565b5f61082b5f83610813565b91506108368261081d565b5f82019050919050565b5f61084a82610820565b9150819050919050565b5f819050919050565b61086681610854565b82525050565b610875816102ec565b82525050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f6108ad8261087b565b6108b78185610885565b93506108c7818560208601610895565b6108d081610364565b840191505092915050565b5f6060820190506108ee5f83018661085d565b6108fb602083018561086c565b818103604083015261090d81846108a3565b905094935050505056fea2646970667358221220c8c1249609242f6af584fc1ec1558364b1d4c5fff1a3012b2dc5a3ee4e4e7cf264736f6c63430008210033
84 changes: 84 additions & 0 deletions util/testutil/fvm_precompiles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package testutil

import (
_ "embed"
"encoding/binary"
"encoding/hex"
"strings"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)

//go:embed fvm_resolve_address_bytecode.txt
var fvmResolveAddressBytecode string

//go:embed fvm_call_actor_by_id_bytecode.txt
var fvmCallActorByIDBytecode string

//go:embed fvm_call_actor_by_address_bytecode.txt
var fvmCallActorByAddressBytecode string

var (
FVMResolveAddress = common.HexToAddress("0xfe00000000000000000000000000000000000001")
FVMCallActorByAddress = common.HexToAddress("0xfe00000000000000000000000000000000000003")
FVMCallActorByID = common.HexToAddress("0xfe00000000000000000000000000000000000005")
)

// SetupFVMPrecompiles deploys narrow Filecoin precompile mocks on an Anvil fork.
//
// These mocks are useful for tests that would otherwise revert on empty Filecoin
// precompile addresses. They cover address resolution plus minimal actor-call
// happy paths, but they do not emulate DataCap / VerifReg flows end-to-end.
func SetupFVMPrecompiles(t *testing.T, rpcURL string) {
t.Helper()
anvilSetCode(t, rpcURL, FVMResolveAddress, mustDecodeHex(t, fvmResolveAddressBytecode))
anvilSetCode(t, rpcURL, FVMCallActorByID, mustDecodeHex(t, fvmCallActorByIDBytecode))
anvilSetCode(t, rpcURL, FVMCallActorByAddress, mustDecodeHex(t, fvmCallActorByAddressBytecode))
t.Log("FVM mock precompiles deployed")
}

// RegisterActorID adds an EVM address -> actor ID mapping to the mocked
// RESOLVE_ADDRESS precompile using the Filecoin delegated-address encoding.
func RegisterActorID(t *testing.T, rpcURL string, evmAddr common.Address, actorID uint64) {
t.Helper()

filAddr := encodeEAMDelegatedAddress(evmAddr)

// ABI encode mockResolveAddress(bytes,uint64)
selector := crypto.Keccak256([]byte("mockResolveAddress(bytes,uint64)"))[:4]
data := make([]byte, 4+32+32+32+32)
copy(data[0:4], selector)
data[4+31] = 64
binary.BigEndian.PutUint64(data[4+32+24:4+32+32], actorID)
data[4+64+31] = byte(len(filAddr))
copy(data[4+96:4+96+len(filAddr)], filAddr)

funderAddr := crypto.PubkeyToAddress(AnvilFunderKey(t).PublicKey)
AnvilImpersonate(t, rpcURL, funderAddr)
SendImpersonatedTx(t, rpcURL, funderAddr, FVMResolveAddress, data)
t.Logf("registered actor ID %d for %s", actorID, evmAddr.Hex())
}

func encodeEAMDelegatedAddress(evmAddr common.Address) []byte {
filAddr := make([]byte, 22)
filAddr[0] = 0x04
filAddr[1] = 0x0a
copy(filAddr[2:], evmAddr.Bytes())
return filAddr
}

func anvilSetCode(t *testing.T, rpcURL string, addr common.Address, code []byte) {
t.Helper()
anvilRPC(t, rpcURL, "anvil_setCode", []any{addr.Hex(), "0x" + hex.EncodeToString(code)})
}

func mustDecodeHex(t *testing.T, s string) []byte {
t.Helper()
s = strings.TrimPrefix(strings.TrimSpace(s), "0x")
b, err := hex.DecodeString(s)
require.NoError(t, err, "failed to decode hex bytecode")
return b
}
161 changes: 161 additions & 0 deletions util/testutil/fvm_precompiles_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package testutil

import (
"context"
"math/big"
"testing"
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
)

func TestIntegration_FVMPrecompilesResolveRegisteredAddress(t *testing.T) {
anvil := StartAnvil(t, CalibnetRPC)

SetupFVMPrecompiles(t, anvil.RPCURL)

account0 := crypto.PubkeyToAddress(AnvilFunderKey(t).PublicKey)
RegisterActorID(t, anvil.RPCURL, account0, 5103)

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

client, err := ethclient.DialContext(ctx, anvil.RPCURL)
require.NoError(t, err)
defer client.Close()

result, err := client.CallContract(ctx, ethereum.CallMsg{
To: &FVMResolveAddress,
Data: encodeEAMDelegatedAddress(account0),
}, nil)
require.NoError(t, err)
require.Len(t, result, 32, "resolve precompile should return a single ABI word")
require.EqualValues(t, 5103, decodeUint64Word(result))

for name, addr := range map[string]common.Address{
"RESOLVE_ADDRESS": FVMResolveAddress,
"CALL_ACTOR_BY_ID": FVMCallActorByID,
"CALL_ACTOR_BY_ADDRESS": FVMCallActorByAddress,
} {
code, err := client.CodeAt(ctx, addr, nil)
require.NoError(t, err)
require.NotEmpty(t, code, "%s should have code deployed", name)
}
}

func TestIntegration_FVMActorCallPrecompilesAcceptMinimalSupportedRequests(t *testing.T) {
anvil := StartAnvil(t, CalibnetRPC)

SetupFVMPrecompiles(t, anvil.RPCURL)

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

client, err := ethclient.DialContext(ctx, anvil.RPCURL)
require.NoError(t, err)
defer client.Close()

result, err := client.CallContract(ctx, ethereum.CallMsg{
To: &FVMCallActorByID,
Data: packCallActorByIDInput(t, 0, 99),
}, nil)
require.NoError(t, err)
exitCode, returnCodec, returnValue := decodeActorCallResponse(t, result)
require.Zero(t, exitCode)
require.Zero(t, returnCodec)
require.Empty(t, returnValue)

account0 := crypto.PubkeyToAddress(AnvilFunderKey(t).PublicKey)
result, err = client.CallContract(ctx, ethereum.CallMsg{
To: &FVMCallActorByAddress,
Data: packCallActorByAddressInput(t, 0, encodeEAMDelegatedAddress(account0)),
}, nil)
require.NoError(t, err)
exitCode, returnCodec, returnValue = decodeActorCallResponse(t, result)
require.Zero(t, exitCode)
require.Zero(t, returnCodec)
require.Empty(t, returnValue)
}

func TestIntegration_FVMResolveUnregisteredAddressReturnsEmpty(t *testing.T) {
anvil := StartAnvil(t, CalibnetRPC)

SetupFVMPrecompiles(t, anvil.RPCURL)

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

client, err := ethclient.DialContext(ctx, anvil.RPCURL)
require.NoError(t, err)
defer client.Close()

key, err := crypto.GenerateKey()
require.NoError(t, err)
unregistered := crypto.PubkeyToAddress(key.PublicKey)

result, err := client.CallContract(ctx, ethereum.CallMsg{
To: &FVMResolveAddress,
Data: encodeEAMDelegatedAddress(unregistered),
}, nil)
require.NoError(t, err)
require.Empty(t, result, "vendored resolve-address mock returns no data for unknown addresses")
}

func decodeUint64Word(result []byte) uint64 {
return new(big.Int).SetBytes(result).Uint64()
}

func packCallActorByIDInput(t *testing.T, methodNum uint64, target uint64) []byte {
t.Helper()
args := abi.Arguments{
{Type: mustABIType(t, "uint64")},
{Type: mustABIType(t, "uint256")},
{Type: mustABIType(t, "uint64")},
{Type: mustABIType(t, "uint64")},
{Type: mustABIType(t, "bytes")},
{Type: mustABIType(t, "uint64")},
}
data, err := args.Pack(methodNum, big.NewInt(0), uint64(0), uint64(0), []byte{}, target)
require.NoError(t, err)
return data
}

func packCallActorByAddressInput(t *testing.T, methodNum uint64, actorAddress []byte) []byte {
t.Helper()
args := abi.Arguments{
{Type: mustABIType(t, "uint64")},
{Type: mustABIType(t, "uint256")},
{Type: mustABIType(t, "uint64")},
{Type: mustABIType(t, "uint64")},
{Type: mustABIType(t, "bytes")},
{Type: mustABIType(t, "bytes")},
}
data, err := args.Pack(methodNum, big.NewInt(0), uint64(0), uint64(0), []byte{}, actorAddress)
require.NoError(t, err)
return data
}

func decodeActorCallResponse(t *testing.T, result []byte) (int64, uint64, []byte) {
t.Helper()
args := abi.Arguments{
{Type: mustABIType(t, "int256")},
{Type: mustABIType(t, "uint64")},
{Type: mustABIType(t, "bytes")},
}
values, err := args.Unpack(result)
require.NoError(t, err)
require.Len(t, values, 3)
return values[0].(*big.Int).Int64(), values[1].(uint64), values[2].([]byte)
}

func mustABIType(t *testing.T, typ string) abi.Type {
t.Helper()
parsed, err := abi.NewType(typ, "", nil)
require.NoError(t, err)
return parsed
}
Loading
Loading