diff --git a/service/dealpusher/ddo_integration_test.go b/service/dealpusher/ddo_integration_test.go index f8d7831e..5da77f0d 100644 --- a/service/dealpusher/ddo_integration_test.go +++ b/service/dealpusher/ddo_integration_test.go @@ -2,11 +2,20 @@ package dealpusher import ( "context" + "math/big" "testing" "time" + "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" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" ) @@ -73,10 +82,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 +100,209 @@ 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() - // 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)) + 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) } -// 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 +// 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: 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(clientSigner) + require.True(t, ok) + t.Logf("Client wallet (Anvil account 0): %s", evmSigner.EVMAddress().Hex()) + + // --- 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: 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) + + parsedABI, err := abi.JSON(strings.NewReader(ddocontract.DDOClientABI)) + require.NoError(t, err) + + // 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) + // Replace selector: createAllocationRequests -> mockCreateRawAllocationRequests (0x76e92deb) + mockSelector := common.FromHex("0x76e92deb") + copy(realCallData[0:4], mockSelector) + + 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("Mock allocation ID: %d", allocationIDs[0]) + + _ = mockFacet +} + +// 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) + + 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) + []ddotypes.TokenConfig{{ + Token: paymentToken, + PricePerBytePerEpoch: big.NewInt(1), + IsActive: true, + }}, + ) + require.NoError(t, err) + + testutil.SendImpersonatedTx(t, rpcURL, owner, ddoAddr, callData) +} + +// 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 +} 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 +}