From db14f3e6bf33b1862e3e0524e0afc1388b6f8324 Mon Sep 17 00:00:00 2001 From: anjor Date: Wed, 1 Apr 2026 11:45:47 +0100 Subject: [PATCH 1/4] Add DDO E2E integration tests with Anvil fork helpers Exercise the full DDO deal lifecycle against a calibnet Anvil fork: - SP registration via contract owner impersonation - USDFC token transfer and payment setup (deposit + operator approval) - CreateAllocations (documents ActorNotFound limitation on Anvil forks) Also adds reusable Anvil test helpers (impersonation, contract owner reads, ERC20 transfers, impersonated tx sending) and the calibnet FF miner t0178773 constant. --- service/dealpusher/ddo_integration_test.go | 245 ++++++++++++++++++--- util/testutil/calibnet.go | 3 + util/testutil/evm.go | 186 ++++++++++++++++ 3 files changed, 408 insertions(+), 26 deletions(-) create mode 100644 util/testutil/evm.go diff --git a/service/dealpusher/ddo_integration_test.go b/service/dealpusher/ddo_integration_test.go index f8d7831e..a41320e7 100644 --- a/service/dealpusher/ddo_integration_test.go +++ b/service/dealpusher/ddo_integration_test.go @@ -2,11 +2,19 @@ package dealpusher import ( "context" + "math/big" "testing" "time" + "strings" + + ddocontract "github.com/Eastore-project/ddo-client/pkg/contract/ddo" + "github.com/data-preservation-programs/go-synapse/signer" "github.com/data-preservation-programs/singularity/util/testutil" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" ) @@ -73,10 +81,9 @@ func TestIntegration_DDOWalletFunding(t *testing.T) { t.Logf("funded wallet %s with %s wei", addr.Hex(), balance.String()) } -// TestIntegration_DDOValidateSP attempts to validate an SP on calibnet. -// This test exercises the contract read path. Without a registered SP, -// ValidateSP returns an empty (inactive) config — we verify the call -// succeeds and returns a well-formed response. +// TestIntegration_DDOValidateSP exercises the contract read path for SP +// validation, first with a bogus provider (should be inactive) and then +// with the real calibnet FF miner t0178773. func TestIntegration_DDOValidateSP(t *testing.T) { anvil := startCalibnetFork(t) rpcURL := anvil.RPCURL @@ -92,30 +99,216 @@ func TestIntegration_DDOValidateSP(t *testing.T) { require.NoError(t, err) defer ddo.Close() - // Use a known-invalid provider ID — should return inactive, not error. - // When a real calibnet SP is registered, this test should be updated - // to use that provider ID and assert IsActive=true. - cfg, err := ddo.ValidateSP(ctx, 99999) + // Bogus provider — should return inactive, not error. + bogus, err := ddo.ValidateSP(ctx, 99999) require.NoError(t, err) - require.NotNil(t, cfg) + require.NotNil(t, bogus) t.Logf("ValidateSP(99999): active=%v, minPiece=%d, maxPiece=%d", - cfg.IsActive, cfg.MinPieceSize, cfg.MaxPieceSize) + bogus.IsActive, bogus.MinPieceSize, bogus.MaxPieceSize) + + // Real calibnet FF miner t0178773. + real, err := ddo.ValidateSP(ctx, testutil.CalibnetDDOProviderActorID) + require.NoError(t, err) + require.NotNil(t, real) + t.Logf("ValidateSP(%d): active=%v, minPiece=%d, maxPiece=%d, minTerm=%d, maxTerm=%d", + testutil.CalibnetDDOProviderActorID, + real.IsActive, real.MinPieceSize, real.MaxPieceSize, + real.MinTermLen, real.MaxTermLen) + if real.IsActive { + require.Greater(t, real.MaxPieceSize, uint64(0)) + } +} + +// TestIntegration_DDORegisterSPOnFork verifies that we can register an SP +// in the DDO contract on an Anvil fork by impersonating the contract owner. +func TestIntegration_DDORegisterSPOnFork(t *testing.T) { + anvil := startCalibnetFork(t) + rpcURL := anvil.RPCURL + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + ddoAddr := common.HexToAddress(testutil.CalibnetDDOContract) + usdfcAddr := common.HexToAddress(testutil.CalibnetUSDFC) + + // Read the DDO contract owner + owner := testutil.ReadContractOwner(t, rpcURL, ddoAddr) + t.Logf("DDO contract owner: %s", owner.Hex()) + + // Impersonate the owner and fund it with gas + testutil.AnvilImpersonate(t, rpcURL, owner) + testutil.FundEVMWallet(t, rpcURL, owner, testutil.OneEther) + + // Register the SP via impersonated eth_sendTransaction + registerSPViaImpersonation(t, rpcURL, ddoAddr, owner, testutil.CalibnetDDOProviderActorID, usdfcAddr) + + // Verify the SP is now registered and active + ddo, err := NewOnChainDDO(ctx, rpcURL, + testutil.CalibnetDDOContract, + testutil.CalibnetPaymentsContract, + testutil.CalibnetUSDFC, + ) + require.NoError(t, err) + defer ddo.Close() + + cfg, err := ddo.ValidateSP(ctx, testutil.CalibnetDDOProviderActorID) + require.NoError(t, err) + require.True(t, cfg.IsActive, "SP should be active after registration") + require.Greater(t, cfg.MaxPieceSize, uint64(0)) + t.Logf("Registered SP %d: active=%v, minPiece=%d, maxPiece=%d", + testutil.CalibnetDDOProviderActorID, cfg.IsActive, cfg.MinPieceSize, cfg.MaxPieceSize) +} + +// TestIntegration_DDOFullDealFlow exercises the complete DDO deal lifecycle: +// register SP → fund wallet → ensure payments → create allocations → parse IDs. +func TestIntegration_DDOFullDealFlow(t *testing.T) { + anvil := startCalibnetFork(t) + rpcURL := anvil.RPCURL + + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + + ddoAddr := common.HexToAddress(testutil.CalibnetDDOContract) + usdfcAddr := common.HexToAddress(testutil.CalibnetUSDFC) + + // --- Step 1: Register the SP on the fork --- + owner := testutil.ReadContractOwner(t, rpcURL, ddoAddr) + testutil.AnvilImpersonate(t, rpcURL, owner) + testutil.FundEVMWallet(t, rpcURL, owner, testutil.OneEther) + registerSPViaImpersonation(t, rpcURL, ddoAddr, owner, testutil.CalibnetDDOProviderActorID, usdfcAddr) + t.Log("SP registered in DDO contract") + + // --- Step 2: Initialize OnChainDDO client --- + ddo, err := NewOnChainDDO(ctx, rpcURL, + testutil.CalibnetDDOContract, + testutil.CalibnetPaymentsContract, + testutil.CalibnetUSDFC, + ) + require.NoError(t, err) + defer ddo.Close() + + // Verify SP is active + spCfg, err := ddo.ValidateSP(ctx, testutil.CalibnetDDOProviderActorID) + require.NoError(t, err) + require.True(t, spCfg.IsActive) + t.Logf("SP config: minPiece=%d, maxPiece=%d, minTerm=%d, maxTerm=%d", + spCfg.MinPieceSize, spCfg.MaxPieceSize, spCfg.MinTermLen, spCfg.MaxTermLen) + + // --- Step 3: Create and fund test wallet --- + testKey, testAddr := testutil.GenerateTestKey(t) + testutil.FundEVMWallet(t, rpcURL, testAddr, new(big.Int).Mul(testutil.OneEther, big.NewInt(100))) + t.Logf("Test wallet: %s", testAddr.Hex()) + + // Transfer USDFC from Anvil's pre-funded account 0 (which holds tokens + // on the calibnet fork) to the test wallet. + usdfcAmount := new(big.Int).Mul(big.NewInt(10), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) // 10 USDFC + testutil.TransferERC20(t, rpcURL, usdfcAddr, testAddr, usdfcAmount) + t.Log("Funded test wallet with USDFC") + + // Create EVM signer from test key + secp256k1Signer, err := signer.NewSecp256k1SignerFromECDSA(testKey) + require.NoError(t, err) + evmSigner, ok := signer.AsEVM(secp256k1Signer) + require.True(t, ok) + require.Equal(t, testAddr, evmSigner.EVMAddress()) + + // --- Step 4: Build test pieces --- + // Use a valid piece CID (commp) + pieceCID := calculateCommp(t, generateRandomBytes(1000), 2048) + pieces := []DDOPieceSubmission{{ + PieceCID: pieceCID, + PieceSize: 2048, + ProviderID: testutil.CalibnetDDOProviderActorID, + DownloadURL: "https://example.test/piece/" + pieceCID.String(), + }} + + cfg := DDOSchedulingConfig{ + BatchSize: 10, + ConfirmationDepth: 1, + PollingInterval: 100 * time.Millisecond, + TermMin: 518400, + TermMax: 5256000, + ExpirationOffset: 172800, + } + + // --- Step 5: EnsurePayments --- + err = ddo.EnsurePayments(ctx, evmSigner, pieces, cfg) + require.NoError(t, err) + t.Log("EnsurePayments succeeded — deposit and operator approval completed on-chain") + + // --- Step 6: CreateAllocations --- + // NOTE: CreateAllocations calls into Filecoin's built-in actor system to + // resolve the caller's actor ID. On an Anvil fork, a freshly generated + // key has no actor entry, so the contract reverts with ActorNotFound. + // This limitation means the full allocation flow can only be tested with + // an address that has a real calibnet actor ID, or on a live calibnet node. + queuedTx, err := ddo.CreateAllocations(ctx, evmSigner, pieces, cfg) + if err != nil { + t.Logf("CreateAllocations reverted (expected on Anvil fork with fresh key): %v", err) + t.Log("The payment setup path (deposit, approval, operator) was validated successfully.") + t.Log("Full allocation creation requires a calibnet actor ID for the sender.") + return + } + + // If we get here (e.g., running with a real calibnet actor), verify the rest. + require.NotEmpty(t, queuedTx.Hash) + t.Logf("CreateAllocations tx: %s", queuedTx.Hash) + + // --- Step 7: WaitForConfirmations --- + receipt, err := ddo.WaitForConfirmations(ctx, queuedTx.Hash, 1, 100*time.Millisecond) + require.NoError(t, err) + require.EqualValues(t, 1, receipt.Status, "allocation tx should succeed") + t.Logf("Allocation tx confirmed: block=%d, gas=%d", receipt.BlockNumber, receipt.GasUsed) + + // --- Step 8: ParseAllocationIDs --- + allocationIDs, err := ddo.ParseAllocationIDs(ctx, queuedTx.Hash) + require.NoError(t, err) + require.Len(t, allocationIDs, 1, "should get exactly 1 allocation ID for 1 piece") + t.Logf("Allocation ID: %d", allocationIDs[0]) +} - // TODO: Once a calibnet SP is registered in the DDO contract, add a - // test here with the real provider actor ID and assert: - // require.True(t, cfg.IsActive) - // require.Greater(t, cfg.MaxPieceSize, uint64(0)) +// registerSPViaImpersonation registers an SP in the DDO contract using +// Anvil's account impersonation. The caller must have already called +// AnvilImpersonate and FundEVMWallet for the owner address. +func registerSPViaImpersonation( + t *testing.T, + rpcURL string, + ddoAddr, owner common.Address, + actorID uint64, + paymentToken common.Address, +) { + t.Helper() + + // ABI-encode the registerSP call + abiJSON := ddocontract.DDOClientABI + parsedABI, err := abi.JSON(strings.NewReader(abiJSON)) + require.NoError(t, err) + + type tokenConfig struct { + Token common.Address + PricePerBytePerEpoch *big.Int + IsActive bool + } + callData, err := parsedABI.Pack("registerSP", + actorID, + owner, // payment address + uint64(512), // min piece size + uint64(1<<35), // max piece size (32 GiB) + int64(172800), // min term (~60 days) + int64(5256000), // max term (~5 years) + []tokenConfig{{ + Token: paymentToken, + PricePerBytePerEpoch: big.NewInt(1), + IsActive: true, + }}, + ) + require.NoError(t, err) + + testutil.SendImpersonatedTx(t, rpcURL, owner, ddoAddr, callData) } -// TODO: TestIntegration_DDOFullDealFlow -// This test requires a registered, active SP on calibnet. Once FF provides -// the SP and it's registered in the DDO contract: -// -// 1. Fork calibnet via Anvil -// 2. Fund a test wallet with FIL -// 3. Create a test preparation with a piece in the database -// 4. Create a DDO schedule pointing to the funded wallet and the SP -// 5. Run the deal pusher schedule -// 6. Verify allocation was created on-chain -// 7. Initialize DDOTrackingClient -// 8. Verify allocation tracking returns the correct status +// calculateCommp and generateRandomBytes are defined in dealpusher_test.go +// and available within the same test package. + +// Ensure cid is used (it's needed by calculateCommp) +var _ = cid.Undef diff --git a/util/testutil/calibnet.go b/util/testutil/calibnet.go index c214aa49..d95d12f5 100644 --- a/util/testutil/calibnet.go +++ b/util/testutil/calibnet.go @@ -7,4 +7,7 @@ const ( CalibnetDDOContract = "0x889fD50196BE300D06dc4b8F0F17fdB0af587095" CalibnetPaymentsContract = "0x09a0fDc2723fAd1A7b8e3e00eE5DF73841df55a0" CalibnetUSDFC = "0xb3042734b608a1B16e9e86B374A3f3e389B4cDf0" + + // CalibnetDDOProviderActorID is the FF calibnet miner t0178773. + CalibnetDDOProviderActorID uint64 = 178773 ) diff --git a/util/testutil/evm.go b/util/testutil/evm.go new file mode 100644 index 00000000..19a508e7 --- /dev/null +++ b/util/testutil/evm.go @@ -0,0 +1,186 @@ +package testutil + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "math/big" + "net/http" + "testing" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/require" +) + +// AnvilImpersonate enables Anvil's impersonation for the given address, +// allowing transactions to be sent from it without a private key. +func AnvilImpersonate(t *testing.T, rpcURL string, addr common.Address) { + t.Helper() + anvilRPC(t, rpcURL, "anvil_impersonateAccount", []any{addr.Hex()}) + t.Cleanup(func() { + anvilRPC(t, rpcURL, "anvil_stopImpersonatingAccount", []any{addr.Hex()}) + }) +} + +// ReadContractOwner reads the Diamond contract owner by calling the owner() function. +func ReadContractOwner(t *testing.T, rpcURL string, contractAddr common.Address) common.Address { + t.Helper() + ctx := context.Background() + client, err := ethclient.DialContext(ctx, rpcURL) + require.NoError(t, err) + defer client.Close() + + selector := crypto.Keccak256([]byte("owner()"))[:4] + result, err := client.CallContract(ctx, ethereum.CallMsg{ + To: &contractAddr, + Data: selector, + }, nil) + require.NoError(t, err) + require.True(t, len(result) >= 32, "owner() returned %d bytes", len(result)) + return common.BytesToAddress(result[12:32]) +} + +// TransferERC20 sends ERC20 tokens from Anvil's pre-funded account 0 to +// a recipient. This works because account 0 already has a real private key +// (the well-known Anvil test key). +func TransferERC20(t *testing.T, rpcURL string, tokenAddr, recipient common.Address, amount *big.Int) { + t.Helper() + ctx := context.Background() + + client, err := ethclient.DialContext(ctx, rpcURL) + require.NoError(t, err) + defer client.Close() + + funderKey := AnvilFunderKey(t) + funderAddr := crypto.PubkeyToAddress(funderKey.PublicKey) + + // ABI-encode transfer(address,uint256) + transferSelector := crypto.Keccak256([]byte("transfer(address,uint256)"))[:4] + data := make([]byte, 4+64) + copy(data[0:4], transferSelector) + copy(data[4+12:4+32], recipient.Bytes()) + amount.FillBytes(data[4+32 : 4+64]) + + nonce, err := client.PendingNonceAt(ctx, funderAddr) + require.NoError(t, err) + chainID, err := client.ChainID(ctx) + require.NoError(t, err) + gasPrice, err := client.SuggestGasPrice(ctx) + require.NoError(t, err) + + tx := types.NewTx(&types.LegacyTx{ + Nonce: nonce, + To: &tokenAddr, + Value: big.NewInt(0), + Gas: 100000, + GasPrice: gasPrice, + Data: data, + }) + + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), funderKey) + require.NoError(t, err) + + err = client.SendTransaction(ctx, signedTx) + require.NoError(t, err) + + for range 30 { + receipt, err := client.TransactionReceipt(ctx, signedTx.Hash()) + if err == nil { + require.EqualValues(t, 1, receipt.Status, "ERC20 transfer failed") + return + } + time.Sleep(100 * time.Millisecond) + } + t.Fatal("ERC20 transfer not mined after 3s") +} + +// SendImpersonatedTx sends a transaction from an impersonated account via +// Anvil's eth_sendTransaction and waits for the receipt. The caller must +// have already called AnvilImpersonate for the from address. +func SendImpersonatedTx(t *testing.T, rpcURL string, from, to common.Address, data []byte) common.Hash { + t.Helper() + ctx := context.Background() + + client, err := ethclient.DialContext(ctx, rpcURL) + require.NoError(t, err) + defer client.Close() + + nonce, err := client.PendingNonceAt(ctx, from) + require.NoError(t, err) + gasPrice, err := client.SuggestGasPrice(ctx) + require.NoError(t, err) + + type txParams struct { + From string `json:"from"` + To string `json:"to"` + Data string `json:"data"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Nonce string `json:"nonce"` + } + + params := txParams{ + From: from.Hex(), + To: to.Hex(), + Data: fmt.Sprintf("0x%x", data), + Gas: fmt.Sprintf("0x%x", 500000), + GasPrice: fmt.Sprintf("0x%x", gasPrice), + Nonce: fmt.Sprintf("0x%x", nonce), + } + + var txHash string + result := anvilRPCResult(t, rpcURL, "eth_sendTransaction", []any{params}) + err = json.Unmarshal(result, &txHash) + require.NoError(t, err, "failed to parse tx hash: %s", string(result)) + + hash := common.HexToHash(txHash) + for range 30 { + receipt, err := client.TransactionReceipt(ctx, hash) + if err == nil { + require.EqualValues(t, 1, receipt.Status, "impersonated tx %s failed", txHash) + return hash + } + time.Sleep(100 * time.Millisecond) + } + t.Fatalf("impersonated tx %s not mined after 3s", txHash) + return hash +} + +func anvilRPC(t *testing.T, rpcURL, method string, params []any) { + t.Helper() + anvilRPCResult(t, rpcURL, method, params) +} + +func anvilRPCResult(t *testing.T, rpcURL, method string, params []any) json.RawMessage { + t.Helper() + + body, _ := json.Marshal(map[string]any{ + "jsonrpc": "2.0", + "method": method, + "params": params, + "id": 1, + }) + resp, err := http.Post(rpcURL, "application/json", bytes.NewReader(body)) + require.NoError(t, err) + defer resp.Body.Close() + require.Equal(t, 200, resp.StatusCode) + + var rpcResp struct { + Result json.RawMessage `json:"result"` + Error *struct { + Code int `json:"code"` + Message string `json:"message"` + } `json:"error"` + } + require.NoError(t, json.NewDecoder(resp.Body).Decode(&rpcResp)) + if rpcResp.Error != nil { + t.Fatalf("rpc %s failed: %s", method, rpcResp.Error.Message) + } + return rpcResp.Result +} From 2314ace3fde2f73d84314f1cd61736219070aac6 Mon Sep 17 00:00:00 2001 From: anjor Date: Wed, 1 Apr 2026 14:07:20 +0100 Subject: [PATCH 2/4] Use Anvil account 0 as DDO client; document precompile limitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch from generating a fresh test key to using Anvil's pre-funded account 0, which already has USDFC tokens and deposited funds on the calibnet fork. This removes the need for ERC20 transfers. CreateAllocations still reverts because Anvil forks lack the Filecoin resolve_address precompile (0xFe00...0001) — this is a fundamental limitation of using a standard EVM fork for FEVM contract testing, not an account issue. --- service/dealpusher/ddo_integration_test.go | 38 ++++++++-------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/service/dealpusher/ddo_integration_test.go b/service/dealpusher/ddo_integration_test.go index a41320e7..4b25bfa1 100644 --- a/service/dealpusher/ddo_integration_test.go +++ b/service/dealpusher/ddo_integration_test.go @@ -194,23 +194,14 @@ func TestIntegration_DDOFullDealFlow(t *testing.T) { t.Logf("SP config: minPiece=%d, maxPiece=%d, minTerm=%d, maxTerm=%d", spCfg.MinPieceSize, spCfg.MaxPieceSize, spCfg.MinTermLen, spCfg.MaxTermLen) - // --- Step 3: Create and fund test wallet --- - testKey, testAddr := testutil.GenerateTestKey(t) - testutil.FundEVMWallet(t, rpcURL, testAddr, new(big.Int).Mul(testutil.OneEther, big.NewInt(100))) - t.Logf("Test wallet: %s", testAddr.Hex()) - - // Transfer USDFC from Anvil's pre-funded account 0 (which holds tokens - // on the calibnet fork) to the test wallet. - usdfcAmount := new(big.Int).Mul(big.NewInt(10), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) // 10 USDFC - testutil.TransferERC20(t, rpcURL, usdfcAddr, testAddr, usdfcAmount) - t.Log("Funded test wallet with USDFC") - - // Create EVM signer from test key - secp256k1Signer, err := signer.NewSecp256k1SignerFromECDSA(testKey) + // --- Step 3: Use Anvil's pre-funded account 0 as client --- + // Account 0 already has USDFC tokens and deposited funds on calibnet. + clientKey := testutil.AnvilFunderKey(t) + clientSigner, err := signer.NewSecp256k1SignerFromECDSA(clientKey) require.NoError(t, err) - evmSigner, ok := signer.AsEVM(secp256k1Signer) + evmSigner, ok := signer.AsEVM(clientSigner) require.True(t, ok) - require.Equal(t, testAddr, evmSigner.EVMAddress()) + t.Logf("Client wallet (Anvil account 0): %s", evmSigner.EVMAddress().Hex()) // --- Step 4: Build test pieces --- // Use a valid piece CID (commp) @@ -237,20 +228,19 @@ func TestIntegration_DDOFullDealFlow(t *testing.T) { t.Log("EnsurePayments succeeded — deposit and operator approval completed on-chain") // --- Step 6: CreateAllocations --- - // NOTE: CreateAllocations calls into Filecoin's built-in actor system to - // resolve the caller's actor ID. On an Anvil fork, a freshly generated - // key has no actor entry, so the contract reverts with ActorNotFound. - // This limitation means the full allocation flow can only be tested with - // an address that has a real calibnet actor ID, or on a live calibnet node. + // NOTE: CreateAllocations calls into Filecoin's resolve_address precompile + // (0xFe00...0001) to resolve the caller's actor ID. Anvil forks don't have + // Filecoin precompiles — they're a standard EVM, not FEVM — so this call + // always reverts with ActorNotFound regardless of which account is used. + // Full allocation testing requires a real Filecoin node (calibnet or mainnet). queuedTx, err := ddo.CreateAllocations(ctx, evmSigner, pieces, cfg) if err != nil { - t.Logf("CreateAllocations reverted (expected on Anvil fork with fresh key): %v", err) - t.Log("The payment setup path (deposit, approval, operator) was validated successfully.") - t.Log("Full allocation creation requires a calibnet actor ID for the sender.") + t.Logf("CreateAllocations reverted (expected — Anvil lacks Filecoin precompiles): %v", err) + t.Log("Payment setup (deposit, approval, operator) validated successfully.") + t.Log("Allocation creation requires a real FEVM node, not an Anvil fork.") return } - // If we get here (e.g., running with a real calibnet actor), verify the rest. require.NotEmpty(t, queuedTx.Hash) t.Logf("CreateAllocations tx: %s", queuedTx.Hash) From 5d2010ef839fd6e3fbe0f3c29d2184713a07d015 Mon Sep 17 00:00:00 2001 From: anjor Date: Wed, 1 Apr 2026 14:40:53 +0100 Subject: [PATCH 3/4] Use ddotypes.TokenConfig instead of redefined ABI struct Remove local tokenConfig struct in registerSPViaImpersonation and use ddotypes.TokenConfig from the ddo-client SDK directly, avoiding brittle ABI redefinition. --- service/dealpusher/ddo_integration_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/service/dealpusher/ddo_integration_test.go b/service/dealpusher/ddo_integration_test.go index 4b25bfa1..66bf5c00 100644 --- a/service/dealpusher/ddo_integration_test.go +++ b/service/dealpusher/ddo_integration_test.go @@ -9,6 +9,7 @@ import ( "strings" ddocontract "github.com/Eastore-project/ddo-client/pkg/contract/ddo" + ddotypes "github.com/Eastore-project/ddo-client/pkg/types" "github.com/data-preservation-programs/go-synapse/signer" "github.com/data-preservation-programs/singularity/util/testutil" "github.com/ethereum/go-ethereum/accounts/abi" @@ -274,11 +275,6 @@ func registerSPViaImpersonation( parsedABI, err := abi.JSON(strings.NewReader(abiJSON)) require.NoError(t, err) - type tokenConfig struct { - Token common.Address - PricePerBytePerEpoch *big.Int - IsActive bool - } callData, err := parsedABI.Pack("registerSP", actorID, owner, // payment address @@ -286,7 +282,7 @@ func registerSPViaImpersonation( uint64(1<<35), // max piece size (32 GiB) int64(172800), // min term (~60 days) int64(5256000), // max term (~5 years) - []tokenConfig{{ + []ddotypes.TokenConfig{{ Token: paymentToken, PricePerBytePerEpoch: big.NewInt(1), IsActive: true, From 2c82cab448da9a36fd04d04b8fd5ebd0897b6776 Mon Sep 17 00:00:00 2001 From: anjor Date: Wed, 1 Apr 2026 15:11:21 +0100 Subject: [PATCH 4/4] Deploy MockAllocationFacet to bypass DataCap on Anvil forks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real createAllocationRequests calls DataCapAPI.transfer() which requires Filecoin's CALL_ACTOR_ID precompile — unavailable on Anvil. This deploys the ddo-client's MockAllocationFacet on the Anvil fork via Diamond cut, then calls mockCreateRawAllocationRequests which skips DataCap and emits AllocationCreated events with mock IDs. The full E2E flow now validates end-to-end: SP registration → payment setup → mock allocation → event parsing --- service/dealpusher/ddo_integration_test.go | 52 +++-- .../mock_allocation_facet_bytecode.txt | 1 + util/testutil/mock_ddo.go | 216 ++++++++++++++++++ 3 files changed, 247 insertions(+), 22 deletions(-) create mode 100644 util/testutil/mock_allocation_facet_bytecode.txt create mode 100644 util/testutil/mock_ddo.go diff --git a/service/dealpusher/ddo_integration_test.go b/service/dealpusher/ddo_integration_test.go index 66bf5c00..5da77f0d 100644 --- a/service/dealpusher/ddo_integration_test.go +++ b/service/dealpusher/ddo_integration_test.go @@ -228,34 +228,42 @@ func TestIntegration_DDOFullDealFlow(t *testing.T) { require.NoError(t, err) t.Log("EnsurePayments succeeded — deposit and operator approval completed on-chain") - // --- Step 6: CreateAllocations --- - // NOTE: CreateAllocations calls into Filecoin's resolve_address precompile - // (0xFe00...0001) to resolve the caller's actor ID. Anvil forks don't have - // Filecoin precompiles — they're a standard EVM, not FEVM — so this call - // always reverts with ActorNotFound regardless of which account is used. - // Full allocation testing requires a real Filecoin node (calibnet or mainnet). - queuedTx, err := ddo.CreateAllocations(ctx, evmSigner, pieces, cfg) - if err != nil { - t.Logf("CreateAllocations reverted (expected — Anvil lacks Filecoin precompiles): %v", err) - t.Log("Payment setup (deposit, approval, operator) validated successfully.") - t.Log("Allocation creation requires a real FEVM node, not an Anvil fork.") - return - } + // --- Step 6: Deploy MockAllocationFacet --- + // The real createAllocationRequests calls DataCapAPI.transfer() which + // requires Filecoin built-in actors (CALL_ACTOR_ID precompile). Anvil + // doesn't have those, so we deploy the MockAllocationFacet which skips + // the DataCap path and generates mock allocation IDs. + mockFacet := testutil.DeployMockAllocationFacet(t, rpcURL, ddoAddr, owner) + + // --- Step 7: Call mockCreateAllocationRequests --- + // ABI-encode the pieceInfos for the mock function call. + pieceInfos, err := ddo.buildPieceInfos(pieces, cfg) + require.NoError(t, err) - require.NotEmpty(t, queuedTx.Hash) - t.Logf("CreateAllocations tx: %s", queuedTx.Hash) + parsedABI, err := abi.JSON(strings.NewReader(ddocontract.DDOClientABI)) + require.NoError(t, err) - // --- Step 7: WaitForConfirmations --- - receipt, err := ddo.WaitForConfirmations(ctx, queuedTx.Hash, 1, 100*time.Millisecond) + // Use mockCreateRawAllocationRequests which skips DataCap AND payment + // rails — it only emits AllocationCreated events with mock IDs. + // Same parameter type as createAllocationRequests. + realCallData, err := parsedABI.Pack("createAllocationRequests", pieceInfos) require.NoError(t, err) - require.EqualValues(t, 1, receipt.Status, "allocation tx should succeed") - t.Logf("Allocation tx confirmed: block=%d, gas=%d", receipt.BlockNumber, receipt.GasUsed) + // Replace selector: createAllocationRequests -> mockCreateRawAllocationRequests (0x76e92deb) + mockSelector := common.FromHex("0x76e92deb") + copy(realCallData[0:4], mockSelector) - // --- Step 8: ParseAllocationIDs --- - allocationIDs, err := ddo.ParseAllocationIDs(ctx, queuedTx.Hash) + clientAddr := evmSigner.EVMAddress() + testutil.AnvilImpersonate(t, rpcURL, clientAddr) + mockTxHash := testutil.SendImpersonatedTx(t, rpcURL, clientAddr, ddoAddr, realCallData) + t.Logf("mockCreateAllocationRequests tx: %s", mockTxHash.Hex()) + + // --- Step 8: Parse AllocationCreated events --- + allocationIDs, err := ddo.ParseAllocationIDs(ctx, mockTxHash.Hex()) require.NoError(t, err) require.Len(t, allocationIDs, 1, "should get exactly 1 allocation ID for 1 piece") - t.Logf("Allocation ID: %d", allocationIDs[0]) + t.Logf("Mock allocation ID: %d", allocationIDs[0]) + + _ = mockFacet } // registerSPViaImpersonation registers an SP in the DDO contract using diff --git a/util/testutil/mock_allocation_facet_bytecode.txt b/util/testutil/mock_allocation_facet_bytecode.txt new file mode 100644 index 00000000..ce4dbab3 --- /dev/null +++ b/util/testutil/mock_allocation_facet_bytecode.txt @@ -0,0 +1 @@ +0x60808060405234601557613dbe908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c8063115103bd146101045780632b8e3274146100ff5780633e7b404c146100fa57806359a84b5c146100f5578063617864ed146100f057806376e92deb146100eb5780637cf3e5fe146100e6578063815a8f40146100e1578063868e10c4146100dc5780638d55fc54146100d7578063a5098b03146100d2578063a694b76c146100cd578063b44039a0146100c8578063bc9cc30f146100c35763bdc87581146100be575f80fd5b6112ac565b611255565b6111fa565b611176565b61103a565b610f4b565b610dea565b610c90565b610c37565b610a5e565b61088d565b61075c565b61068e565b6105db565b610394565b634e487b7160e01b5f52604160045260245ffd5b60c081019081106001600160401b0382111761013857604052565b610109565b604081019081106001600160401b0382111761013857604052565b606081019081106001600160401b0382111761013857604052565b601f909101601f19168101906001600160401b0382119082101761013857604052565b604051906101a560c083610173565b565b604051906101a561010083610173565b604051906101a561012083610173565b6001600160401b03811161013857601f01601f191660200190565b81601f82011215610226576020813591016101fc826101c7565b9261020a6040519485610173565b8284528282011161022657815f92602092838601378301015290565b5f80fd5b602060031982011261022657600435906001600160401b03821161022657610254916004016101e2565b90565b6001600160401b031690565b6001600160401b03169052565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b604081016040825282518091526020606083019301905f5b818110610366575050506020818303910152815180825260208201916020808360051b8301019401925f915b8383106102e757505050505090565b9091929394602080600192601f19858203018652885190848060401b03825116815260a0806103238585015160c08786015260c0850190610270565b93878060401b036040820151166040850152606081015160070b6060850152608081015160070b6080850152015160070b910152970193019301919392906102d8565b825180516001600160401b0390811687526020918201511681870152604090950194909201916001016102ac565b34610226576103a23661022a565b60026103ad826129ce565b910361058c576103bd9082612a3a565b9190916103c98361177c565b915f5b8481106103f25750906103de91612a61565b506103ee60405192839283610294565b0390f35b909161040960069161040261174b565b5084612a3a565b910361057d576104676006610461602a60ff61045261042b610446978a612f13565b6104369891986134b8565b5061043f6134b8565b508a6133de565b96919390931614613893565b6001600160401b0316146138f0565b84612ff8565b9490936104976001600160f81b031961049061048288612fda565b516001600160f81b03191690565b161561393b565b61049f6134b8565b966104b26104ad87516134db565b611a46565b885260015b86518110156104ef57806104d06104826001938a612fe7565b6104e88b51916104df846134db565b905f1a92612fe7565b53016104b7565b50939294509590946105019087612f13565b61050e9088939293612f20565b6105189089612f20565b610525908a939293612f20565b94909351610531610196565b9861053c908a6115da565b602089015261054e90604089016115da565b60070b606087015260070b608086015260070b60a08501529261057182866117df565b526001019390936103cc565b6302c9fa8b60e61b5f5260045ffd5b630d4a593360e41b5f5260045ffd5b600435906001600160401b038216820361022657565b602435906001600160401b038216820361022657565b35906001600160401b038216820361022657565b34610226576020366003190112610226576105f461059b565b6105fd81611559565b80549091906001600160a01b038116156106495760e01c60ff1661063a5760059190910180546001600160401b0319169055610638906120cb565b005b6373b10d1160e01b5f5260045ffd5b636afc507960e11b5f5260045ffd5b6001600160a01b031690565b600435906001600160a01b038216820361022657565b35906001600160a01b038216820361022657565b34610226576040366003190112610226576106a7610664565b6106af6105b1565b5f516020613d495f395f51905f52546001600160a01b031633036106de576106d961063892611502565b61153a565b60405162461bcd60e51b815260206004820152602260248201527f4c69624469616d6f6e643a204d75737420626520636f6e7472616374206f776e60448201526132b960f11b6064820152608490fd5b9261025496959260c09592855260208501526040840152606083015260808201528160a08201520190610270565b346102265760403660031901126102265761077561059b565b61078a6107856024355b92611559565b6115e9565b61079e61079a6040830151151590565b1590565b61087e5760c00180511561086f576107cb6107c65f516020613d295f395f51905f5254610658565b610658565b916001600160a01b03831615610860576107ff925f92518360405180968195829463179a817f60e31b84526004840161172f565b03925af1801561085b575f5f915f5f5f915f93610828575b604051965086956103ee958761072e565b50505050505061084c6103ee913d805f833e6108448183610173565b8101906116ed565b93955085949392909190610817565b611740565b6311e1e64360e31b5f5260045ffd5b637b2e30af60e01b5f5260045ffd5b63aeabe9e560e01b5f5260045ffd5b346102265760203660031901126102265760206001600160401b036108b86108b3610664565b611502565b5416604051908152f35b6001600160401b0381116101385760051b60200190565b35908160070b820361022657565b602060031982011261022657600435906001600160401b03821161022657806023830112156102265781600401359161091f836108c2565b9261092d6040519485610173565b8084526024602085019160051b830101918383116102265760248101915b83831061095a57505050505090565b82356001600160401b038111610226578201906101008287036023190112610226576109846101a7565b9060248301356001600160401b038111610226578760246109a7928601016101e2565b82526109b5604484016105c7565b60208301526109c6606484016105c7565b60408301526109d7608484016108d9565b60608301526109e860a484016108d9565b60808301526109f960c484016108d9565b60a083015260e4830135916001600160401b03831161022657610a3761010485610a2c8b6024602099988a9901016101e2565b60c08501520161067a565b60e082015281520192019161094b565b604090610254939281528160208201520190610270565b3461022657610a6c366108e7565b5f815115610c2857610a7e825161177c565b4360070b905f5b8451811015610c0e57610a9881866117df565b516020810190610ab0610aab8351610257565b610257565b15610bff576040810195610ac7610aab8851610257565b15610bf05760a082015160070b610ade908761180c565b8751610ae990610257565b938351928151610af890610257565b9560608601948551610b0a9060070b90565b60808801988951610b1b9060070b90565b92610b24610196565b94610b2f90866115da565b6020850152610b4190604085016115da565b60070b606083015260070b6080820152600784900b60a0820152610b65888a6117df565b52610b7087896117df565b508151610b7c90610257565b610b8590610257565b610b8e91611877565b9851610b9990610257565b9184519151610ba790610257565b935160070b955160070b9460c0015190604051958694600160401b6001900316973396610bd49587611884565b035f516020613d695f395f51905f5290600a93a4600101610a85565b6356b254fd60e01b5f5260045ffd5b63aec6d71560e01b5f5260045ffd5b83610c1883611d8e565b906103ee60405192839283610a47565b630137363b60e51b5f5260045ffd5b3461022657610c453661022a565b8051810190602081830312610226576020810151906001600160401b038211610226576020610c7c92816103ee95019201016116a7565b604051918291602083526020830190610270565b3461022657608036600319011261022657610ca961059b565b60243590604435606435610cc5610cc05f9461157e565b6118d2565b90815115610ddb5780610dc057508051915b828110610ce957604051848152602090f35b84610d0f610785610d0a610cfd85876117df565b516001600160401b031690565b611559565b6040810151151580610db3575b610d2b575b5050600101610cd7565b610d6f9295915f9160c0610d4f6107c65f516020613d295f395f51905f5254610658565b9101518360405180978195829463179a817f60e31b84526004840161172f565b03925af190811561085b57600192610d8c92610d96575b50611a1d565b939050845f610d21565b610da9903d805f833e6108448183610173565b5050505050610d86565b5060c08101511515610d1c565b610dcd610dd59184611877565b825190612029565b91610cd7565b63263f7f4d60e01b5f5260045ffd5b3461022657606036600319011261022657610e0361059b565b610e0b6105b1565b50604435906001600160401b03821161022657610e3e6103ee91610e3863ffffffff9436906004016101e2565b90611a78565b604080519590931685526001600160401b0390911660208501526060918401829052839291830190610270565b6020606081604085019363ffffffff81511686520151936040838201528451809452019201905f5b818110610ea05750505090565b8251805163ffffffff9081168652602091820151168186015260409094019390920191600101610e93565b90602082526040610f00610eeb8351606060208701526080860190610e6b565b6020840151858203601f190184870152610e6b565b910151916060601f1982840301910152602080835192838152019201905f5b818110610f2c5750505090565b82516001600160401b0316845260209384019390920191600101610f1f565b3461022657610f593661022a565b610f61611cfb565b50610f6a611cfb565b906003610f76826129ce565b910361101a57610f95610f8c610fa1928461318d565b9085528361318d565b90602085015282612a3a565b9091610fac836108c2565b91610fba6040519384610173565b838352601f19610fc9856108c2565b01366020850137604085019283525f905b848210610fef57604051806103ee8882610ecb565b829161100060019261101295612f13565b949061100d8388516117df565b6115da565b019091610fda565b6335cbd21160e21b5f5260045ffd5b906020610254928181520190610270565b34610226576020366003190112610226576004356001600160401b0381116102265736602382011215610226578060040135611075816108c2565b916110836040519384610173565b8183526024602084019260051b820101903682116102265760248101925b8284106110c0576103ee6110b486611d8e565b60405191829182611029565b83356001600160401b0381116102265782019060c0602319833603011261022657604051906110ee8261011d565b6110fa602484016105c7565b82526044830135916001600160401b0383116102265761116660c46020956111298796602436918401016101e2565b86850152611139606482016105c7565b604085015261114a608482016108d9565b606085015261115b60a482016108d9565b6080850152016108d9565b60a08201528152019301926110a1565b346102265760403660031901126102265761118f61059b565b61119d61078560243561077f565b6111ad61079a6040830151151590565b61087e5760c00180511561086f576107ff915f916111db6107c65f516020613d295f395f51905f5254610658565b90518360405180968195829463179a817f60e31b84526004840161172f565b346102265760403660031901126102265761121361059b565b61121b6105b1565b9061122581611559565b80546001600160a01b038116156106495760e01c60ff1661063a57610638926005611250920161153a565b6120cb565b3461022657611263366108e7565b5f90815b81518310156112a1576001600160401b03602061128485856117df565b51015116810180911161129c57600190920191611267565b6117f8565b602090604051908152f35b34610226576112ba366108e7565b5f6112c48261239d565b815115610c28575f516020613d295f395f51905f52546001600160a01b03906112f0906107c690610658565b161561086057611300825161177c565b904360070b915f5b84518110156114f85761131b81866117df565b5192602084019361132f610aab8651610257565b15610bff576040810191611346610aab8451610257565b15610bf05760a082015160070b61135d908861180c565b91835161136990610257565b92815192885161137890610257565b946060840194855161138a9060070b90565b6080860197885161139b9060070b90565b926113a4610196565b946113af90866115da565b60208501526113c190604085016115da565b60070b606083015260070b6080820152600783900b60a08201526113e5888a6117df565b526113f087896117df565b5088516113fc90610257565b61140590610257565b61140e91611877565b976114198742611877565b61142290611831565b61142b90610257565b335f9081527fb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ee1602052604090209096908761146591611d28565b86815161147190610257565b61147a9061157e565b9061148491611d28565b61148e8785612689565b505161149990610257565b91835191516114a790610257565b945160070b955160070b9360c0015190604051948594600160401b600190031698600160401b60019003169733976114df9587611884565b035f516020613d695f395f51905f5291a4600101611308565b50610c1890611d8e565b6001600160a01b03165f9081527fb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ee56020526040902090565b80546001600160401b0319166001600160401b03909216919091179055565b6001600160401b03165f9081525f516020613d095f395f51905f526020526040902090565b6001600160401b03165f9081527fb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ee26020526040902090565b9060018060401b03165f5260205260405f2090565b60a01c6001600160401b031690565b6001600160401b039091169052565b906101a561169d60056115fa6101b7565b9461164361163a825461161c61160f82610658565b6001600160a01b03168a52565b611631611628826115cb565b60208b016115da565b60e01c60ff1690565b15156040880152565b60018101546060870152611682611679600283015461167461166482610658565b6001600160a01b031660808b0152565b6115cb565b60a088016115da565b600381015460c0870152600481015460e08701520154610257565b61010084016115da565b81601f82011215610226578051906116be826101c7565b926116cc6040519485610173565b8284526020838301011161022657815f9260208093018386015e8301015290565b9160c0838303126102265782519260208101519260408201519260608301519260808101519260a082015160018060401b0381116102265761025492016116a7565b908152602081019190915260400190565b6040513d5f823e3d90fd5b604051906117588261011d565b5f60a083828152606060208201528260408201528260608201528260808201520152565b90611786826108c2565b6117936040519182610173565b82815280926117a4601f19916108c2565b01905f5b8281106117b457505050565b6020906117bf61174b565b828285010152016117a8565b634e487b7160e01b5f52603260045260245ffd5b80518210156117f35760209160051b010190565b6117cb565b634e487b7160e01b5f52601160045260245ffd5b600791820b910b019060016001603f1b0319821260016001603f1b0383131761129c57565b906001820180921161129c57565b906002820180921161129c57565b906004820180921161129c57565b906008820180921161129c57565b906020820180921161129c57565b9190820180921161129c57565b9261189f61025497959296939660c0865260c0860190610270565b9560018060401b0316602085015260070b604084015260070b606083015260070b608082015260a0818403910152610270565b604051815480825290929183906118f060208301915f5260205f2090565b925f905b8060038301106119af576101a594549181811061198d575b818110611967575b818110611941575b1061192a575b500383610173565b9061193a8160209360c01c610263565b015f611922565b9260019060209061195f81608087901c6001600160401b0316610263565b01930161191c565b9260019060209061198581604087901c6001600160401b0316610263565b019301611914565b926001906020906119a7816001600160401b038716610263565b01930161190c565b845491935091600491600191608091611a0f906119d5836001600160401b038316610263565b6119ec60208401868060401b038360401c16610263565b611a0260408401868060401b0383871c16610263565b606083019060c01c610263565b0194019201859293916118f4565b5f19811461129c5760010190565b60405190611a3a602083610173565b5f808352366020840137565b90611a50826101c7565b611a5d6040519182610173565b8281528092611a6e601f19916101c7565b0190602036910137565b60ff7fb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ee75416611cd3576001600160401b031663de180de38103611ace575050611abf61203b565b611ac7611a2b565b5f915f9190565b637942460303611cc457611ae461079a33612e85565b611cb557611b01611af433611502565b546001600160401b031690565b90611b0b816129ce565b92611b14612bc5565b93611b286001600160401b03841686613549565b5f905b838210611b48575050505050611b4090612e43565b5f9160519190565b90611b75611b64611b5c611b6d9488612a3a565b905087612f13565b87949194612f20565b905086612a3a565b92611b896001600160401b03831689613549565b5f5b87838210611ba0575050505060010190611b2b565b90611bd4611bcb84611bc2611bba611bdb9a600197612a3a565b90508d612ff8565b8d939193612f13565b8d949194612ff8565b9990612e9e565b5091611be683611559565b905f94825491611bf86107c684610658565b15159283611ca5575b83611c85575b5082611c6e575b5081611c4b575b50611c2e575b505050611c28908b61311e565b01611b8b565b611c409350906005611250920161153a565b81611c28845f611c1b565b9050611c5a60028301546115cb565b604088901b8890039081169116145f611c15565b90915087830154906020815191012014905f611c0e565b611c909193506115cb565b604089901b8990038e8116911614915f611c07565b60e081901c60ff16159350611c01565b63c7b21e1d60e01b5f5260045ffd5b63369a557760e11b5f5260045ffd5b63d93c066560e01b5f5260045ffd5b60405190611cef8261013d565b60606020835f81520152565b60405190611d0882610158565b6060604083611d15611ce2565b8152611d1f611ce2565b60208201520152565b80549190600160401b83101561013857600183018082558310156117f3575f5260205f208260021c019160c083549160061b169160018060401b0316821b9160018060401b03901b1916179055565b60070b60016001603f1b0319811461129c575f0390565b906001611da4611d9e8451612b55565b82611877565b835f915b8151831015611f4b57611eb8611e82611e3b84611e09611e04610aab6040611e2c8b611e26611e0f8f9d611e0960019f611eee9f611df687611df0611e0494610aab94611877565b956117df565b51516001600160401b031690565b612b55565b90611877565b611e096020611e1e858c6117df565b510151612b95565b966117df565b5101516001600160401b031690565b88865f611e5f611e596060611e5085876117df565b51015160070b90565b60070b90565b12611f31576060611e50611e7692611e09946117df565b868060401b0316612b55565b5f611e95611e596080611e50898d6117df565b12611f1757611e09611eac6080611e50888c6117df565b858060401b0316612b55565b5f611ecb611e5960a0611e50888c6117df565b12611ef857611e09611ee260a0611e50878b6117df565b848060401b0316612b55565b9101908490611da8565b611e09611f12611f0d60a0611e50888c6117df565b611d77565b611ee2565b611e09611f2c611f0d6080611e50898d6117df565b611eac565b611f0d6060611e50611f4693611e09956117df565b611e76565b611f5b9250611f60939150611877565b612bf5565b611f6981612c1f565b611f7c611f768451610257565b82613549565b5f5b8351811015612013578061200d61200760a0611f9c600195896117df565b51611fa687612c2a565b611fb9611fb38251610257565b886135df565b611fc7602082015188612c35565b611fd7611fb36040830151610257565b611fee611fe8606083015160070b90565b88612d58565b611fff611fe8608083015160070b90565b015160070b90565b84612d58565b01611f7e565b506102549192506120245f82613549565b612e43565b9080821015612036575090565b905090565b600660ff60981b01193301611cc4577f10aa319ed8cad9bceb033c0c2788c4ae17469ac844e4c6e2c2e20e74ca8a7be86060604051602081526011602082015270446174614361702052656365697665642160781b6040820152a1565b908160011b918083046002149015171561129c57565b8181029291811591840414171561129c57565b5f91031261022657565b6120e2815f516020613d095f395f51905f526115b6565b61210060048201546120fa610aab60028501546115cb565b906120ae565b815460ff60e01b1916600160e01b1782559061212c6107c65f516020613d295f395f51905f5254610658565b916003820192835490803b15610226576040516325f4fa8d60e21b81526004810192909252602482018390525f60448301819052908290606490829084905af1801561085b5761224f575b506121926107c65f516020613d295f395f51905f5254610658565b91835492803b156102265760405163de07b8bb60e01b815260048101949094526201518060248501525f60448501819052908490606490829084905af190811561085b577f42a81c5beb0db16cea3e364d8af9fb4c276cd7bb484418ace1eda77651c6208c9361220892612235575b50546115cb565b9254604080515f815260208101929092528101919091526001600160401b039283169390921691606090a3565b806122435f61224993610173565b806120c1565b5f612201565b806122435f61225d93610173565b5f612177565b90815461226f816108c2565b9261227d6040519485610173565b81845260208401905f5260205f205f915b83831061229b5750505050565b600360206001926040516122ae81610158565b848060a01b038654168152848601548382015260ff6002870154161515604082015281520192019201919061228e565b60405190919060e08101906001600160401b03821181831017610138576101a59160405260c061239660038396612330815460018060a01b038116875260018060401b039060a01c16602087016115da565b61237d612373600183015461235061234782610257565b60408a016115da565b61236a6123608260401c60070b90565b60070b60608a0152565b60801c60070b90565b60070b6080870152565b61238960028201612263565b60a0860152015460ff1690565b1515910152565b905f5b8251811015612572576123b86040611e2c83866117df565b906123e36107c66123d6845f516020613ce95f395f51905f526115b6565b546001600160a01b031690565b156125635761240f61079a6003612407855f516020613ce95f395f51905f526115b6565b015460ff1690565b6125545761243261242d835f516020613ce95f395f51905f526115b6565b6122de565b6124416020611e2c84886117df565b612451610aab6020840151610257565b6001600160401b03909116108015612523575b612514576124776060611e5084886117df565b612488611e59606084015160070b90565b9060070b129081156124e8575b506124d9576124d26001926124be60e06124af85896117df565b5101516001600160a01b031690565b905f516020613d295f395f51905f5261328a565b50016123a0565b63156234b160e01b5f5260045ffd5b9050612509611e59608061250081611e50878b6117df565b93015160070b90565b9060070b135f612495565b63cb0741b560e01b5f5260045ffd5b506125336020611e2c84886117df565b612543610aab6040840151610257565b6001600160401b0390911611612464565b6308f55eab60e11b5f5260045ffd5b63e1e08b6160e01b5f5260045ffd5b509050565b90816020910312610226575190565b80546001600160a01b0319166001600160a01b03909216919091179055565b8054600160a01b600160e01b03191660a09290921b600160a01b600160e01b0316919091179055565b60056126826101006101a5946125ed6125e78251610658565b86612586565b6126036125fd6020830151610257565b866125a5565b61262d6126136040830151151590565b865460ff60e01b191690151560e01b60ff60e01b16178655565b606081015160018601556126676002860161265461264e6080850151610658565b82612586565b61266160a0840151610257565b906125a5565b60c0810151600386015560e081015160048601550151610257565b910161153a565b91906126a56107c65f516020613d295f395f51905f5254610658565b6001600160a01b038116156108605760408401906126dc61242d6126c98451610257565b5f516020613ce95f395f51905f526115b6565b6126e96107c68251610658565b156125635760e086019360206127026107c68751610658565b61270c8451610658565b947fb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ede54915f61274e60018060a01b035f516020613d495f395f51905f52541690565b604051631f3ef1bd60e31b81526001600160a01b03938416600482015233602482015298831660448a01523060648a01526084890194909452921660a4870152859160c4918391905af192831561085b575f9361299d575b5082966127c36107c65f516020613d295f395f51905f5254610658565b907fb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9edf5491803b156102265760405163de07b8bb60e01b8152600481018790525f60248201819052604482019490945292908390606490829084905af191821561085b577f630e67d11e84268aae4845a8ee6031c9d101c1c141b9b0d63fd8d2a81898734b9461291161287f9361294d9361295496612989575b50896128e38c6128da61288661287f6128758651610257565b6124be8551610658565b9451610257565b946128a6602061289e83518281519101209551610658565b920151610257565b926128bf6128b26101b7565b33815297602089016115da565b5f604088015260608701526001600160a01b03166080860152565b60a084016115da565b8960c083015260e08201525f61010082015261290c875f516020613d095f395f51905f526115b6565b6125ce565b612947856106d9897fb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ee8905f5260205260405f2090565b51610658565b9651610658565b604080519485526001600160401b0391821660208601529116908301526001600160a01b0392831693909216913391606090a4565b806122435f61299793610173565b5f61285c565b6129c091935060203d6020116129c7575b6129b88183610173565b810190612577565b915f6127a6565b503d6129ae565b9060ff6129dd5f6004946133de565b949192909216036129f5576001600160401b03169190565b60405162461bcd60e51b815260206004820152601f60248201527f696e76616c6964206d616a20286578706563746564204d616a417272617929006044820152606490fd5b6129dd6004939260ff926133de565b60405190612a568261013d565b5f6020838281520152565b9190612a6d9083612a3a565b9290612a78816108c2565b91612a866040519384610173565b818352601f19612a95836108c2565b015f5b818110612b3e575050905f915b818310612ab3575050509190565b909194612ac260039183612a3a565b9103612b2f57612aea95612b27612af3612b0d612ae160019587612f13565b879b919b612f13565b87939193612f20565b905099858060401b0316612b07858a6117df565b516115da565b838060401b03166020612b2084896117df565b51016115da565b019190612aa5565b6344b7062b60e11b5f5260045ffd5b602090612b49612a49565b82828801015201612a98565b60178111612b635750600190565b60ff8111612b715750600290565b61ffff8111612b805750600390565b63ffffffff1015612b9057600990565b600590565b51612b9f81612b55565b90810180911161129c5790565b60405190612bb98261013d565b5f602083606081520152565b604051612bd18161013d565b5f612bda612bac565b808352612bf061010060208501928484526134f8565b505290565b5f60405191612c038361013d565b612bf0612c0e612bac565b9182855260208501928484526134f8565b60026101a591613549565b60066101a591613549565b602090612c826021612cc094612c4b8451613670565b50612c5684516136e0565b506040519485915f828401528051918291018484015e81015f838201520301601f198101845283610173565b81516001600160401b031660178111612cc3578151612cb891612cb290612cab60405b60ff1690565b1760ff1690565b90613745565b505b51613b7d565b50565b60ff8111612cf0578151612cea9190612ce290601860405b1790613745565b508251613aee565b50612cba565b61ffff8111612d15578151612cea9190612d0d9060196040612cdb565b508251613a80565b63ffffffff8111612d3c578151612cea9190612d3490601a6040612cdb565b508251613a10565b8151612cea9190612d5090601b6040612cdb565b50825161399c565b600782900b915f8312612d79576101a592506001600160401b0316906135df565b50901960016001603f1b0319811260016001603f1b0382131761129c576001600160401b031660178111612dbb579051612cc091612cb290612cab6020612ca5565b60ff8111612dde578151612cc09290612dd79060186020612cdb565b5051613aee565b61ffff8111612e02578151612cc09290612dfb9060196020612cdb565b5051613a80565b63ffffffff8111612e28578151612cc09290612e2190601a6020612cdb565b5051613a10565b8151612cc09290612e3c90601b6020612cdb565b505161399c565b6020810151612e5157515190565b60405162461bcd60e51b815260206004820152600c60248201526b24b73b30b634b21021a127a960a11b6044820152606490fd5b6001600160401b0390612e9790611502565b5416151590565b90612eab5f60ff936133de565b93909116612ec0576001600160401b03169190565b60405162461bcd60e51b815260206004820152602560248201527f696e76616c6964206d616a20286578706563746564204d616a556e7369676e6560448201526464496e742960d81b6064820152608490fd5b60ff9291612eab916133de565b60ff9291612f2d916133de565b9390911660018114908115612fb6575b5015612f53576001600160401b031660070b9190565b60405162461bcd60e51b815260206004820152603560248201527f696e76616c6964206d616a20286578706563746564204d616a5369676e6564496044820152746e74206f72204d616a556e7369676e6564496e742960581b6064820152608490fd5b9050155f612f3d565b15612fc657565b634e487b7160e01b5f52600160045260245ffd5b8051156117f35760200190565b9081518110156117f3570160200190565b919061300490836133de565b60ff819260018060401b031693166006811480918115613113575b50156130b757613085575b506130358282611877565b61303e83611a46565b9482905f5b83831061305857505050509061025491611877565b61307d60019161306b6104828686612fe7565b5f1a613077828c612fe7565b53611a1d565b920191613043565b6002925060ff915061309a6130b191866133de565b6001600160401b0390911694909390911614612fbf565b5f61302a565b60405162461bcd60e51b815260206004820152602e60248201527f696e76616c6964206d616a20286578706563746564204d616a546167206f722060448201526d4d616a42797465537472696e672960901b6064820152608490fd5b60029150145f61301f565b612cc09115613136576015905b519060e01790613745565b60149061312b565b90613148826108c2565b6131556040519182610173565b8281528092613166601f19916108c2565b01905f5b82811061317657505050565b602090613181612a49565b8282850101520161316a565b91909160026131a461319d611ce2565b9483612a3a565b910361327b5763ffffffff6131cd6131bf6131d39385612f13565b9216869063ffffffff169052565b82612a3a565b916131dd8261313e565b91602086019283525f925b8184106131f6575050505090565b9091929361320660029185612a3a565b910361326c5763ffffffff94613263613247876132416132286001968a612f13565b92166132358689516117df565b519063ffffffff169052565b87612f13565b971660206132568487516117df565b51019063ffffffff169052565b019291906131e8565b63fb369fdb60e01b5f5260045ffd5b6352b846db60e01b5f5260045ffd5b600601906001600160a01b036132a96132a383856115b6565b54610658565b1615612563576132be6002916132c4936115b6565b01612263565b5f5b815181101561333a576132ea6132dc82846117df565b51516001600160a01b031690565b6001600160a01b038481169116148061331d575b61330a576001016132c6565b60209250613317916117df565b51015190565b50613335604061332d83856117df565b510151151590565b6132fe565b636c310f7560e11b5f5260045ffd5b1561335057565b60405162461bcd60e51b815260206004820152602560248201527f63616e6e6f742068616e646c6520686561646572732077697468206578747261604482015264203e20323760d81b6064820152608490fd5b156133aa57565b60405162461bcd60e51b815260206004820152600c60248201526b34b73b30b634b21031b137b960a11b6044820152606490fd5b90916133f36133ed84846137f3565b93611831565b91601f600585901c600716941661340c601c8210613349565b601881106134b2576018811461348d5790816019859695931461347657601a8114613456579161344a91613445601b6134509514612fbf565b61386c565b9361185b565b91929190565b5061346963ffffffff9261346f92613845565b9461184d565b9293169190565b5061348761ffff9261346f9261381e565b9461183f565b5061349d6133ed8460ff936137f3565b9216926134ad60188510156133a3565b929190565b92919050565b60405190602082016001600160401b038111838210176101385760405260608252565b5f1981019190821161129c57565b601f1981019190821161129c57565b90613501612bac565b50601f81168061352f575b50806020830152604051908183525f825281016020019081106102265760405290565b6020036020811161129c57810180911161129c575f61350c565b906001600160401b0381166017811161357157509051612cc091612cb290612cab6080612ca5565b905060ff811161358f578151612cc09290612dd79060186080612cdb565b61ffff81116135ac578151612cc09290612dfb9060196080612cdb565b63ffffffff81116135cb578151612cc09290612e2190601a6080612cdb565b8151612cc09290612e3c90601b6080612cdb565b906001600160401b0381166017811161360657509051612cc091612cb290612cab5f612ca5565b905060ff8111613623578151612cc09290612dd79060185f612cdb565b61ffff811161363f578151612cc09290612dfb9060195f612cdb565b63ffffffff811161365d578151612cc09290612e2190601a5f612cdb565b8151612cc09290612e3c90601b5f612cdb565b613678612bac565b50805151600181019081811161129c5760208301518110156136b1575b60d8602084519283010153805182116136ad57505090565b5290565b8160011b8281046002148315171561129c576136da906136d3855191866134f8565b5084613b7d565b50613695565b6136e8612bac565b50805151600181019081811161129c57602083015181101561371d575b602a602084519283010153805182116136ad57505090565b8160011b8281046002148315171561129c5761373f906136d3855191866134f8565b50613705565b9061374e612bac565b50815151600181019182821161129c576020840151821015613781575b602084519283010153805182116136ad57505090565b8260011b8381046002148415171561129c576137aa906137a3865191876134f8565b5085613b7d565b5061376b565b156137b757565b60405162461bcd60e51b8152602060048201526014602482015273736c6963696e67206f7574206f662072616e676560601b6044820152606490fd5b90815191600182019283831161129c57613817936138129110156137b0565b612fe7565b5160f81c90565b90815191600282019283831161129c5760209361383c9110156137b0565b01015160f01c90565b90815191600482019283831161129c576020936138639110156137b0565b01015160e01c90565b90815191600882019283831161129c5760209361388a9110156137b0565b01015160c01c90565b1561389a57565b60405162461bcd60e51b815260206004820152602860248201527f6578706563746564206d616a6f72207479706520746167207768656e207061726044820152671cda5b99c818da5960c21b6064820152608490fd5b156138f757565b606460405162461bcd60e51b815260206004820152602060248201527f657870656374656420746167203432207768656e2070617273696e67206369646044820152fd5b1561394257565b60405162461bcd60e51b815260206004820152602c60248201527f6578706563746564206669727374206279746520746f2062652030207768656e60448201526b081c185c9cda5b99c818da5960a21b6064820152608490fd5b906139a5612bac565b5081515180600801918260081161129c57602084015183116139e8575b835191820160080180516001600160401b0319169091179052805182116136ad57505090565b8260011b8381046002148415171561129c57613a0a906137a3865191876134f8565b506139c2565b90613a19612bac565b5081515180600401918260041161129c5760208401518311613a58575b60048451928301019063ffffffff19825116179052805182116136ad57505090565b8260011b8381046002148415171561129c57613a7a906137a3865191876134f8565b50613a36565b90613a89612bac565b5081515180600201918260021161129c5760208401518311613ac6575b60028451928301019061ffff19825116179052805182116136ad57505090565b8260011b8381046002148415171561129c57613ae8906137a3865191876134f8565b50613aa6565b90613af7612bac565b5081515180600101918260011161129c5760208401518311613b33575b60018451928301019060ff19825116179052805182116136ad57505090565b8260011b8381046002148415171561129c57613b55906137a3865191876134f8565b50613b14565b612cc091613b6b825191836134f8565b50613b74612bac565b50805191613c40565b90613b86612bac565b508051613b91612bac565b50613b9f8251821115613c39565b602083515192613baf8385611877565b828601518111613c22575b855183815196820101958211613c1a575b505001905b6020811015613bf4575f19906020036101000a019081199051169082511617905290565b9091613c0f613c09613c159285518152611869565b93611869565b916134e9565b613bd0565b525f80613bcb565b613c34613c2e82612098565b87613b5b565b613bba565b1561022657565b91613c49612bac565b50613c578251821115613c39565b82515191613c658284611877565b9060208501518211613cd1575b602091855183815196820101958211613cc9575b505001905b6020811015613caf575f19906020036101000a019081199051169082511617905290565b9091613c0f613c09613cc49285518152611869565b613c8b565b525f80613c86565b613ce3613cdd83612098565b86613b5b565b613c7256feb67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ee3b67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9ee0b67062676da20b5506f8d96d5ddfcf27412752438036ea6a1745130e2d1e9eddc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c1320eb73d3d62dd7334a87cb25d74901216bb95b7f7e2973cebd4b58f0339c942baea2646970667358221220dcafcb82998e6177fb1d1ebb084672680477b5bc5842fd1d0cd568d1e4e1afae64736f6c63430008210033 \ No newline at end of file diff --git a/util/testutil/mock_ddo.go b/util/testutil/mock_ddo.go new file mode 100644 index 00000000..d99bb02d --- /dev/null +++ b/util/testutil/mock_ddo.go @@ -0,0 +1,216 @@ +package testutil + +import ( + "context" + _ "embed" + "encoding/hex" + "encoding/json" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/require" +) + +//go:embed mock_allocation_facet_bytecode.txt +var mockAllocationFacetBytecodeHex string + +// MockDDOFacet holds the deployed mock AllocationFacet address and +// provides helpers to call mock-specific functions. +type MockDDOFacet struct { + Address common.Address + RPCURL string +} + +// DeployMockAllocationFacet deploys the MockAllocationFacet contract on +// an Anvil fork and performs a Diamond cut to add mock selectors to the +// DDO Diamond proxy. The caller must have already impersonated the +// Diamond owner. +func DeployMockAllocationFacet(t *testing.T, rpcURL string, ddoAddr, owner common.Address) *MockDDOFacet { + t.Helper() + ctx := context.Background() + + client, err := ethclient.DialContext(ctx, rpcURL) + require.NoError(t, err) + defer client.Close() + + // Deploy the mock facet by sending the creation bytecode from the + // impersonated owner. + bytecodeHex := mockAllocationFacetBytecodeHex + if len(bytecodeHex) > 2 && bytecodeHex[:2] == "0x" { + bytecodeHex = bytecodeHex[2:] + } + + nonce, err := client.PendingNonceAt(ctx, owner) + require.NoError(t, err) + gasPrice, err := client.SuggestGasPrice(ctx) + require.NoError(t, err) + + // Send deployment tx via impersonated account + type txParams struct { + From string `json:"from"` + Data string `json:"data"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + Nonce string `json:"nonce"` + } + params := txParams{ + From: owner.Hex(), + Data: "0x" + bytecodeHex, + Gas: fmt.Sprintf("0x%x", 10_000_000), + GasPrice: fmt.Sprintf("0x%x", gasPrice), + Nonce: fmt.Sprintf("0x%x", nonce), + } + + var txHash string + result := anvilRPCResult(t, rpcURL, "eth_sendTransaction", []any{params}) + err = json.Unmarshal(result, &txHash) + require.NoError(t, err, "deploy mock facet tx: %s", string(result)) + + // Wait for receipt to get contract address + hash := common.HexToHash(txHash) + var mockAddr common.Address + for range 30 { + receipt, err := client.TransactionReceipt(ctx, hash) + if err == nil { + require.EqualValues(t, 1, receipt.Status, "mock facet deployment failed") + mockAddr = receipt.ContractAddress + break + } + time.Sleep(100 * time.Millisecond) + } + require.NotEqual(t, common.Address{}, mockAddr, "mock facet deployment: no contract address") + t.Logf("MockAllocationFacet deployed at %s", mockAddr.Hex()) + + // Diamond cut: add mock selectors to the Diamond proxy. + // diamondCut(FacetCut[] calldata, address, bytes calldata) + // FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors } + // FacetCutAction.Add = 0 + mockSelectors := []string{ + "bdc87581", // mockCreateAllocationRequests + "76e92deb", // mockCreateRawAllocationRequests + "2b8e3274", // mockActivateAllocation + "b44039a0", // mockActivateAllocationWithSector + "3e7b404c", // setMockMiner + "617864ed", // mockMinerActorIds + } + + diamondCutSig := crypto.Keccak256([]byte("diamondCut((address,uint8,bytes4[])[],address,bytes)"))[:4] + + // ABI encode: FacetCut[] with 1 element (Add action) + // This is complex ABI encoding. Let's use a simpler approach via cast. + selectorsHex := "" + for _, s := range mockSelectors { + selectorsHex += s + } + _ = selectorsHex + _ = diamondCutSig + + // Use cast to do the ABI encoding and send the tx + diamondCutViaRPC(t, rpcURL, ddoAddr, owner, mockAddr, mockSelectors) + + return &MockDDOFacet{Address: mockAddr, RPCURL: rpcURL} +} + +// diamondCutViaRPC encodes and sends a diamondCut transaction via impersonation. +func diamondCutViaRPC(t *testing.T, rpcURL string, ddoAddr, owner, facetAddr common.Address, selectors []string) { + t.Helper() + + // Manually ABI-encode diamondCut((address,uint8,bytes4[])[],address,bytes) + // with one FacetCut: { facetAddress, Add(0), selectors } + // + // Layout: + // 4 bytes: function selector + // 32 bytes: offset to FacetCut[] (dynamic) + // 32 bytes: _init address (zero) + // 32 bytes: offset to _calldata (dynamic) + // -- FacetCut[] -- + // 32 bytes: array length (1) + // 32 bytes: offset to first element + // -- FacetCut struct -- + // 32 bytes: facetAddress + // 32 bytes: action (0 = Add) + // 32 bytes: offset to bytes4[] (relative to struct start) + // -- bytes4[] -- + // 32 bytes: array length + // N * 32 bytes: each selector padded to 32 bytes + + funcSig := crypto.Keccak256([]byte("diamondCut((address,uint8,bytes4[])[],address,bytes)"))[:4] + + // Pre-compute sizes + numSelectors := len(selectors) + + // Build calldata manually + data := make([]byte, 0, 4+32*20) // generous initial capacity + + // Function selector + data = append(data, funcSig...) + + // Param 1: offset to FacetCut[] array (3 params * 32 = 96 = 0x60) + data = append(data, padLeft(big.NewInt(0x60).Bytes(), 32)...) + // Param 2: _init address (zero) + data = append(data, padLeft(nil, 32)...) + // Param 3: offset to _calldata (we'll compute after) + // Placeholder — we'll fill this in after computing the FacetCut[] size + calldataOffsetPos := len(data) + data = append(data, padLeft(nil, 32)...) // placeholder + + // FacetCut[] array + // Array length = 1 + data = append(data, padLeft(big.NewInt(1).Bytes(), 32)...) + // Offset to first element (32 bytes from array start) + data = append(data, padLeft(big.NewInt(0x20).Bytes(), 32)...) + + // FacetCut struct (tuple) + // facetAddress + data = append(data, padLeft(facetAddr.Bytes(), 32)...) + // action = 0 (Add) + data = append(data, padLeft(nil, 32)...) + // offset to bytes4[] (relative to struct start = 3 * 32 = 96 = 0x60) + data = append(data, padLeft(big.NewInt(0x60).Bytes(), 32)...) + + // bytes4[] array + data = append(data, padLeft(big.NewInt(int64(numSelectors)).Bytes(), 32)...) + for _, sel := range selectors { + selBytes, err := hex.DecodeString(sel) + require.NoError(t, err) + // bytes4 is left-aligned in 32 bytes + padded := make([]byte, 32) + copy(padded[0:4], selBytes) + data = append(data, padded...) + } + + // Now fill in the _calldata offset + // _calldata is empty bytes, offset points to after all the FacetCut[] data + calldataOffset := len(data) - 4 // subtract function selector + copy(data[calldataOffsetPos:calldataOffsetPos+32], padLeft(big.NewInt(int64(calldataOffset)).Bytes(), 32)) + + // Empty bytes _calldata + data = append(data, padLeft(nil, 32)...) // length = 0 + + SendImpersonatedTx(t, rpcURL, owner, ddoAddr, data) + t.Log("Diamond cut completed — mock selectors added") +} + +// CallMockCreateAllocations calls mockCreateAllocationRequests on the +// DDO Diamond proxy via the impersonated client wallet. +func (m *MockDDOFacet) CallMockCreateAllocations(t *testing.T, from common.Address, ddoAddr common.Address, pieceInfosABI []byte) common.Hash { + t.Helper() + // mockCreateAllocationRequests selector = 0xbdc87581 + selector, _ := hex.DecodeString("bdc87581") + data := append(selector, pieceInfosABI...) + return SendImpersonatedTx(t, m.RPCURL, from, ddoAddr, data) +} + +func padLeft(b []byte, size int) []byte { + if len(b) >= size { + return b[:size] + } + padded := make([]byte, size) + copy(padded[size-len(b):], b) + return padded +}