From d34edaabd34417a636cdc17b75fbdffc56a6dfd7 Mon Sep 17 00:00:00 2001 From: Chris Cassano Date: Tue, 21 Jul 2026 16:08:13 -0700 Subject: [PATCH] refactor(contracts): remove completed #575 PKP-owner backfill machinery The one-time backfill is complete on Base mainnet (all pre-existing PKPs bound; verified 0 unbound, 0 conflicts). Remove the now-spent migration machinery in this facet upgrade: - WritesFacet: drop backfillPkpOwners() and the PkpOwnerBackfilled event. - ViewsFacet.getWalletDerivation: remove the `owner == 0` compatibility fallback so ownership enforcement is unconditional (fail-closed). registerWalletDerivation always sets the owner when it writes pkpData and the backfill bound every legacy PKP, so a non-zero derivation always has a non-zero owner; owner == 0 now reverts instead of serving the path. - Delete the tasks/backfill-pkp-owners.ts hardhat task + its registration. - Keep getPkpOwnerMaster (ongoing audit) and the core first-owner binding. - Update Foundry tests + DiamondDeploy selectors, regenerate alloy bindings, update CHANGELOG. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 7 +- .../AccountConfigFacets/ViewsFacet.sol | 29 +- .../AccountConfigFacets/WritesFacet.sol | 33 -- .../lit_node_express/hardhat.config.ts | 1 - .../tasks/backfill-pkp-owners.ts | 240 ----------- .../lit_node_express/test/Accounts.t.sol | 79 +--- .../test/helpers/DiamondDeploy.sol | 3 +- .../src/accounts/contracts/AccountConfig.json | 59 +-- .../contracts/account_config_contract.rs | 390 +----------------- 9 files changed, 51 insertions(+), 790 deletions(-) delete mode 100644 lit-api-server/blockchain/lit_node_express/tasks/backfill-pkp-owners.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ae46a3dc..fad7dbb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,8 +37,11 @@ doesn't describe endpoints the released server lacks. (`pkpId → master account`): a wallet address can only ever be registered — or re-registered after deletion — by the account that first registered it, closing a cross-account PKP hijack via publicly visible derivation paths - (#575). Requires a one-time on-chain backfill for wallets registered before - the upgrade (`backfillPkpOwners`). + (#575). `getWalletDerivation` enforces the same binding at read time + (fail-closed), so the node never resolves a key for an account that doesn't + own the PKP. The one-time backfill of pre-existing PKPs is complete on-chain; + the migration helper (`backfillPkpOwners`) and the pre-backfill compatibility + fallback have been removed. ## v1.1.10 — 2026-06-22 diff --git a/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/ViewsFacet.sol b/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/ViewsFacet.sol index 0bc00815..9b4ac61e 100644 --- a/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/ViewsFacet.sol +++ b/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/ViewsFacet.sol @@ -167,26 +167,27 @@ contract ViewsFacet { // Cross-account hijack defense (#575). This view is the exact read the // node uses to resolve a derivation path before signing/decrypting, so // enforce the global first-owner binding HERE, not just at registration: - // if the wallet has an owner and the resolving (master) account is not - // that owner, the local pkpData entry is a stale pre-fix hijack - // registration — fail closed instead of leaking the victim's path. - // owner == 0 means a pre-migration wallet not yet backfilled: fall - // through so signing keeps working until backfillPkpOwners runs. + // the resolving (master) account must be the pkpId's owner, otherwise + // the local pkpData entry is a stale hijack registration — fail closed + // instead of leaking the victim's path. + // + // registerWalletDerivation always sets pkpIdToOwnerMaster when it writes + // pkpData, and the one-time #575 backfill bound every pre-existing PKP, + // so a non-zero derivation always has a non-zero owner. An owner of 0 + // here therefore means an unexpected/legacy state and fails closed. AppStorage.AccountConfigStorage storage s = AppStorage.getStorage(); uint256 owner = s.pkpIdToOwnerMaster[walletAddress]; - if (owner != 0) { - uint256 resolvedMaster = s.allApiKeyHashesToMaster[apiKeyHash]; - if (owner != resolvedMaster) { - revert AppStorage.InvalidRequest("PKP owned by another account"); - } + uint256 resolvedMaster = s.allApiKeyHashesToMaster[apiKeyHash]; + if (owner != resolvedMaster) { + revert AppStorage.InvalidRequest("PKP owned by another account"); } return derivation; } - /// @notice Return the master apiKeyHash that first registered a pkpId, or 0 if - /// the pkpId has never been bound (pre-migration wallet or never registered). - /// @dev The binding survives removeWalletDerivation by design; used to audit the - /// global first-owner rule and the one-time backfill migration. + /// @notice Return the master apiKeyHash that owns a pkpId, or 0 if the pkpId + /// has never been registered. + /// @dev The binding survives removeWalletDerivation by design; used to audit + /// the global first-owner rule. function getPkpOwnerMaster(address pkpId) public view returns (uint256) { AppStorage.AccountConfigStorage storage s = AppStorage.getStorage(); return s.pkpIdToOwnerMaster[pkpId]; diff --git a/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/WritesFacet.sol b/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/WritesFacet.sol index c4fb8391..fa049526 100644 --- a/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/WritesFacet.sol +++ b/lit-api-server/blockchain/lit_node_express/contracts/AccountConfigFacets/WritesFacet.sol @@ -68,10 +68,6 @@ contract WritesFacet { uint256 indexed apiKeyHash, address indexed pkpId ); - event PkpOwnerBackfilled( - address indexed pkpId, - uint256 indexed masterHash - ); event UsageApiKeyRemoved( uint256 indexed accountApiKeyHash, uint256 indexed usageApiKeyHash @@ -762,35 +758,6 @@ contract WritesFacet { emit WalletDerivationRemoved(apiKeyHash, pkpId); } - /// @notice One-time migration helper: bind wallets registered before the global - /// owner binding existed to their original master account. - /// @dev Pairs should be derived off-chain from the EARLIEST - /// `WalletDerivationRegistered(masterHash, pkpId, ...)` event per pkpId - /// (first registration wins, matching the rule `registerWalletDerivation` - /// now enforces). Already-bound pkpIds are skipped, never re-assigned, so - /// the call is idempotent and safe to run in batches / re-run. Restricted - /// to the diamond owner or config operator. - function backfillPkpOwners( - address[] calldata pkpIds, - uint256[] calldata masterHashes - ) public { - SecurityLib.revertIfNotConfigOperatorOrOwner(msg.sender); - if (pkpIds.length != masterHashes.length) { - revert AppStorage.InvalidRequest("array length mismatch"); - } - AppStorage.AccountConfigStorage storage s = AppStorage.getStorage(); - for (uint256 i = 0; i < pkpIds.length; i++) { - if (masterHashes[i] == 0) { - revert AppStorage.InvalidRequest("masterHash must be non-zero"); - } - if (s.pkpIdToOwnerMaster[pkpIds[i]] != 0) { - continue; // already bound — never re-assign ownership - } - s.pkpIdToOwnerMaster[pkpIds[i]] = masterHashes[i]; - emit PkpOwnerBackfilled(pkpIds[i], masterHashes[i]); - } - } - function setNodeConfiguration( string memory key, string memory value diff --git a/lit-api-server/blockchain/lit_node_express/hardhat.config.ts b/lit-api-server/blockchain/lit_node_express/hardhat.config.ts index a42910af..c6bc586c 100644 --- a/lit-api-server/blockchain/lit_node_express/hardhat.config.ts +++ b/lit-api-server/blockchain/lit_node_express/hardhat.config.ts @@ -11,7 +11,6 @@ import "./tasks/propose-add-device"; import "./tasks/transfer-ownership"; import "./tasks/verify-compose-hash"; import "./tasks/verify-diamond-facets"; -import "./tasks/backfill-pkp-owners"; const config: HardhatUserConfig = { solidity: { diff --git a/lit-api-server/blockchain/lit_node_express/tasks/backfill-pkp-owners.ts b/lit-api-server/blockchain/lit_node_express/tasks/backfill-pkp-owners.ts deleted file mode 100644 index 6dec3560..00000000 --- a/lit-api-server/blockchain/lit_node_express/tasks/backfill-pkp-owners.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { task } from "hardhat/config"; -import { ethers } from "ethers"; - -// Minimal ABI: the event we scan plus the migration entry points. -const DIAMOND_ABI = [ - "event WalletDerivationRegistered(uint256 indexed apiKeyHash, address indexed pkpId, uint256 derivationPath)", - "function backfillPkpOwners(address[] pkpIds, uint256[] masterHashes)", - "function getPkpOwnerMaster(address pkpId) view returns (uint256)", -]; - -interface FirstRegistration { - pkpId: string; - masterHash: bigint; - blockNumber: number; - txHash: string; - conflicts: bigint[]; // other masters that later registered the same pkpId -} - -/** - * One-time migration for issue #575: wallets registered before the global - * `pkpIdToOwnerMaster` binding existed have no owner entry, so any account - * could still claim them via registerWalletDerivation. This task rebuilds the - * binding from history: it scans every WalletDerivationRegistered event, takes - * the FIRST registration per pkpId (the same rule the contract now enforces), - * and submits backfillPkpOwners in batches. Already-bound pkpIds are skipped - * on-chain, so the task is idempotent and safe to re-run until it reports - * nothing left to bind. - */ -task( - "backfill-pkp-owners", - "Backfill pkpIdToOwnerMaster for wallets registered before the #575 fix" -) - .addParam("diamond", "Diamond proxy contract address") - .addOptionalParam("fromBlock", "Block to start scanning events from", "0") - .addOptionalParam("chunkSize", "getLogs block range per request", "10000") - .addOptionalParam("batchSize", "pkpIds per backfill transaction", "200") - .addOptionalParam( - "confirmations", - "Blocks to stay behind chain head to avoid reorgs (0 = use the 'finalized' tag)", - "0" - ) - .addFlag("execute", "Send the backfill transactions (default is dry-run)") - .addFlag( - "allowConflicts", - "Proceed even if pkpIds were registered by multiple accounts (pre-fix hijacks). Off by default: conflicts are a hard stop under --execute." - ) - .setAction(async (taskArgs, hre) => { - const { diamond: diamondAddress } = taskArgs; - const fromBlock = parseInt(taskArgs.fromBlock, 10); - const chunkSize = parseInt(taskArgs.chunkSize, 10); - const batchSize = parseInt(taskArgs.batchSize, 10); - const confirmations = parseInt(taskArgs.confirmations, 10); - - const rpcUrl = - (hre.network.config as { url?: string }).url || "https://mainnet.base.org"; - const provider = new ethers.JsonRpcProvider(rpcUrl); - const readOnly = new ethers.Contract(diamondAddress, DIAMOND_ABI, provider); - - console.log(`Network: ${hre.network.name}`); - console.log(`Diamond: ${diamondAddress}`); - - // 1. Scan all WalletDerivationRegistered events. The event's first indexed - // arg is the master apiKeyHash (WritesFacet emits masterHash, never a - // usage-key hash), so it is exactly the value pkpIdToOwnerMaster needs. - // Scan to a finalized/confirmed head, not `latest`: a reorg that reranks - // the first registrant would otherwise bind the wrong owner. The scanned - // upper bound is fixed up front so the whole run reasons over one range. - let latestBlock: number; - if (confirmations > 0) { - latestBlock = (await provider.getBlockNumber()) - confirmations; - } else { - const finalized = await provider.getBlock("finalized"); - if (!finalized) { - throw new Error( - "Provider does not support the 'finalized' block tag; pass --confirmations N instead" - ); - } - latestBlock = finalized.number; - } - if (latestBlock < fromBlock) { - console.log("No finalized blocks in range yet. Nothing to do."); - return; - } - console.log( - `Scanning events from block ${fromBlock} to ${latestBlock} (finalized head)...` - ); - - const filter = readOnly.filters.WalletDerivationRegistered(); - const allLogs: ethers.EventLog[] = []; - for (let start = fromBlock; start <= latestBlock; start += chunkSize) { - const end = Math.min(start + chunkSize - 1, latestBlock); - const logs = await readOnly.queryFilter(filter, start, end); - for (const log of logs) allLogs.push(log as ethers.EventLog); - if (end < latestBlock) { - process.stdout.write( - `\r scanned up to block ${end} (${allLogs.length} events)` - ); - } - } - - // Sort explicitly by (blockNumber, transactionIndex, logIndex) rather than - // trusting provider ordering — "first registration wins" is only correct if - // we actually process logs in chain order, and getLogs ordering is not - // guaranteed across or within chunks. - allLogs.sort( - (a, b) => - a.blockNumber - b.blockNumber || - a.transactionIndex - b.transactionIndex || - a.index - b.index - ); - - const firstByPkp = new Map(); - for (const log of allLogs) { - const masterHash = log.args[0] as bigint; - const pkpId = (log.args[1] as string).toLowerCase(); - const existing = firstByPkp.get(pkpId); - if (!existing) { - firstByPkp.set(pkpId, { - pkpId: log.args[1] as string, - masterHash, - blockNumber: log.blockNumber, - txHash: log.transactionHash, - conflicts: [], - }); - } else if ( - existing.masterHash !== masterHash && - !existing.conflicts.includes(masterHash) - ) { - existing.conflicts.push(masterHash); - } - } - console.log( - `\nFound ${allLogs.length} registration events across ${firstByPkp.size} distinct pkpIds.` - ); - - // 2. Surface pkpIds registered by more than one master account. Each is a - // wallet another account also claimed pre-fix — a probable hijack. The - // backfill binds the FIRST registrant, and the hardened getWalletDerivation - // then refuses to serve the later registrant's stale local entry. But a - // conflict still means: (a) verify the first registrant is genuinely the - // rightful owner (an attacker who registered BEFORE the victim would be - // bound as owner here), and (b) the later account's pkpData row should be - // removed. So conflicts are a HARD STOP under --execute unless the - // operator has reviewed them and passes --allow-conflicts. - const conflicted = [...firstByPkp.values()].filter( - (r) => r.conflicts.length > 0 - ); - if (conflicted.length > 0) { - console.log( - `\n⚠️ ${conflicted.length} pkpId(s) were registered by MULTIPLE master accounts (probable pre-fix hijack):` - ); - for (const r of conflicted) { - console.log( - ` ${r.pkpId} first=0x${r.masterHash.toString(16)} (block ${r.blockNumber}, ${r.txHash})` - ); - for (const other of r.conflicts) { - console.log(` also registered by 0x${other.toString(16)}`); - } - } - console.log( - " Backfill binds the FIRST registrant; the later registrant's stale pkpData row must be removed separately." - ); - if (taskArgs.execute && !taskArgs.allowConflicts) { - throw new Error( - `Refusing to --execute with ${conflicted.length} unresolved conflict(s). Review them, remediate the later registrants, then re-run with --allow-conflicts.` - ); - } - } - - // 3. Drop pkpIds that are already bound (post-fix registrations, or a - // previous run of this task). - console.log("\nChecking current on-chain bindings..."); - const toBind: FirstRegistration[] = []; - for (const r of firstByPkp.values()) { - const owner: bigint = await readOnly.getPkpOwnerMaster(r.pkpId); - if (owner === 0n) { - toBind.push(r); - } else if (owner !== r.masterHash) { - console.log( - ` ⚠️ ${r.pkpId} already bound to 0x${owner.toString(16)} which is NOT its first registrant 0x${r.masterHash.toString(16)} — investigate` - ); - } - } - console.log(`${toBind.length} pkpId(s) need backfilling.`); - if (toBind.length === 0) { - console.log("Nothing to do."); - return; - } - - if (!taskArgs.execute) { - console.log("\nDry run (pass --execute to send transactions):"); - for (const r of toBind) { - console.log(` ${r.pkpId} -> 0x${r.masterHash.toString(16)}`); - } - return; - } - - // 4. Send backfillPkpOwners in batches. Caller must be the diamond owner - // or config operator. - const signerKey = - process.env.CONFIG_OPERATOR_PRIVATE_KEY || process.env.OWNER_PRIVATE_KEY; - if (!signerKey) { - throw new Error( - "CONFIG_OPERATOR_PRIVATE_KEY or OWNER_PRIVATE_KEY environment variable is required with --execute" - ); - } - const wallet = new ethers.Wallet(signerKey, provider); - const diamond = new ethers.Contract(diamondAddress, DIAMOND_ABI, wallet); - console.log(`\nSending backfill as ${wallet.address}...`); - - for (let i = 0; i < toBind.length; i += batchSize) { - const batch = toBind.slice(i, i + batchSize); - const tx = await diamond.backfillPkpOwners( - batch.map((r) => r.pkpId), - batch.map((r) => r.masterHash) - ); - console.log( - ` batch ${i / batchSize + 1} (${batch.length} pkpIds): ${tx.hash}` - ); - const receipt = await tx.wait(); - console.log(` confirmed in block ${receipt.blockNumber}`); - } - - // 5. Verify every pair landed. - console.log("\nVerifying..."); - let failures = 0; - for (const r of toBind) { - const owner: bigint = await readOnly.getPkpOwnerMaster(r.pkpId); - if (owner !== r.masterHash) { - failures++; - console.log( - ` ❌ ${r.pkpId}: expected 0x${r.masterHash.toString(16)}, got 0x${owner.toString(16)}` - ); - } - } - if (failures > 0) { - throw new Error(`${failures} binding(s) failed verification`); - } - console.log(`All ${toBind.length} bindings verified. Backfill complete.`); - }); diff --git a/lit-api-server/blockchain/lit_node_express/test/Accounts.t.sol b/lit-api-server/blockchain/lit_node_express/test/Accounts.t.sol index 09ac8ec2..00ca54c4 100644 --- a/lit-api-server/blockchain/lit_node_express/test/Accounts.t.sol +++ b/lit-api-server/blockchain/lit_node_express/test/Accounts.t.sol @@ -409,60 +409,6 @@ contract AccountsTest is BaseTest { assertEq(views_.getPkpOwnerMaster(pkpAddr), victimHash); } - function test_backfillPkpOwners_bindsLegacyWalletsAndBlocksHijack() public { - vm.prank(user); - writes.newChainSecuredAccount("legacy", "legacy"); - uint256 legacyHash = apiKeyHashOf(user); - vm.prank(stranger); - writes.newChainSecuredAccount("attacker", "attacker"); - uint256 attackerHash = apiKeyHashOf(stranger); - - // Simulate a pre-migration wallet: registered in the account but with no - // global owner binding (as if registered before the upgrade). - address legacyPkp = address(0x1E9AC7); - assertEq(views_.getPkpOwnerMaster(legacyPkp), 0); - - address[] memory pkpIds = new address[](1); - pkpIds[0] = legacyPkp; - uint256[] memory masters = new uint256[](1); - masters[0] = legacyHash; - - // Only the diamond owner or config operator may backfill. - vm.prank(stranger); - vm.expectRevert( - abi.encodeWithSelector( - AppStorage.OnlyConfigOperatorOrOwner.selector, - stranger - ) - ); - writes.backfillPkpOwners(pkpIds, masters); - - vm.prank(owner); - writes.backfillPkpOwners(pkpIds, masters); - assertEq(views_.getPkpOwnerMaster(legacyPkp), legacyHash); - - // Backfill never re-assigns an existing binding (idempotent, skip-if-set). - masters[0] = attackerHash; - vm.prank(owner); - writes.backfillPkpOwners(pkpIds, masters); - assertEq(views_.getPkpOwnerMaster(legacyPkp), legacyHash); - - // Post-backfill, the attacker cannot register the legacy wallet... - vm.prank(stranger); - vm.expectRevert( - abi.encodeWithSelector( - AppStorage.InvalidRequest.selector, - "PKP owned by another account" - ) - ); - writes.registerWalletDerivation(attackerHash, legacyPkp, 7, "a", "a"); - - // ...but the legacy owner can (e.g. #450 recovery re-registration). - vm.prank(user); - writes.registerWalletDerivation(legacyHash, legacyPkp, 7, "l", "l"); - assertEq(views_.getWalletDerivation(legacyHash, legacyPkp), 7); - } - /// @dev Storage slot of `pkpIdToOwnerMaster[pkpId]`. The mapping is the last /// field of AccountConfigStorage (field index 18, counting the two /// EnumerableSet fields as 2 slots each) at base slot @@ -510,10 +456,12 @@ contract AccountsTest is BaseTest { views_.getWalletDerivation(attackerHash, pkpAddr); } - function test_getWalletDerivation_legacyUnboundStillReadable() public { - // A pre-migration wallet has a local entry but no owner binding yet - // (owner==0). It must keep resolving so signing doesn't break in the - // window between the facet upgrade and the backfill run. + function test_getWalletDerivation_ownerZeroFailsClosed() public { + // Post-migration the compatibility fallback is gone: enforcement is + // unconditional. A local pkpData entry whose owner binding is 0 (an + // unexpected/legacy state that can no longer occur via the public API, + // since registerWalletDerivation always sets the owner) must fail closed + // rather than leak the path. vm.prank(stranger); writes.newChainSecuredAccount("u", "u"); uint256 hash = apiKeyHashOf(stranger); @@ -521,12 +469,19 @@ contract AccountsTest is BaseTest { vm.prank(stranger); writes.registerWalletDerivation(hash, pkpAddr, 42, "a", "a"); - // Clear the binding to reproduce the not-yet-backfilled legacy state. + // Normal case: owner is set to the account, read resolves. + assertEq(views_.getWalletDerivation(hash, pkpAddr), 42); + + // Force the anomalous owner==0 state and confirm it now reverts. vm.store(address(views_), _pkpOwnerSlot(pkpAddr), bytes32(uint256(0))); assertEq(views_.getPkpOwnerMaster(pkpAddr), 0); - - // owner==0 falls through: the wallet still resolves for its account. - assertEq(views_.getWalletDerivation(hash, pkpAddr), 42); + vm.expectRevert( + abi.encodeWithSelector( + AppStorage.InvalidRequest.selector, + "PKP owned by another account" + ) + ); + views_.getWalletDerivation(hash, pkpAddr); } function test_removeWalletDerivation_unregisteredReverts() public { diff --git a/lit-api-server/blockchain/lit_node_express/test/helpers/DiamondDeploy.sol b/lit-api-server/blockchain/lit_node_express/test/helpers/DiamondDeploy.sol index 5fdb905c..f631d627 100644 --- a/lit-api-server/blockchain/lit_node_express/test/helpers/DiamondDeploy.sol +++ b/lit-api-server/blockchain/lit_node_express/test/helpers/DiamondDeploy.sol @@ -146,7 +146,7 @@ library DiamondDeploy { } function writesSelectors() internal pure returns (bytes4[] memory s) { - s = new bytes4[](21); + s = new bytes4[](20); s[0] = WritesFacet.newChainSecuredAccount.selector; s[1] = WritesFacet.newAccount.selector; s[2] = WritesFacet.convertToChainSecuredAccount.selector; @@ -167,7 +167,6 @@ library DiamondDeploy { s[17] = WritesFacet.registerWalletDerivation.selector; s[18] = WritesFacet.removeWalletDerivation.selector; s[19] = WritesFacet.setNodeConfiguration.selector; - s[20] = WritesFacet.backfillPkpOwners.selector; } function apiConfigSelectors() internal pure returns (bytes4[] memory s) { diff --git a/lit-api-server/src/accounts/contracts/AccountConfig.json b/lit-api-server/src/accounts/contracts/AccountConfig.json index f5a18d8a..81f5c958 100644 --- a/lit-api-server/src/accounts/contracts/AccountConfig.json +++ b/lit-api-server/src/accounts/contracts/AccountConfig.json @@ -190,17 +190,6 @@ "name": "OnlyApiPayerOrOwner", "type": "error" }, - { - "inputs": [ - { - "internalType": "address", - "name": "caller", - "type": "address" - } - ], - "name": "OnlyConfigOperatorOrOwner", - "type": "error" - }, { "inputs": [ { @@ -477,25 +466,6 @@ "name": "PkpAddedToGroup", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "pkpId", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "masterHash", - "type": "uint256" - } - ], - "name": "PkpOwnerBackfilled", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -716,24 +686,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "address[]", - "name": "pkpIds", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "masterHashes", - "type": "uint256[]" - } - ], - "name": "backfillPkpOwners", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -2165,6 +2117,17 @@ "name": "NotContractOwner", "type": "error" }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + } + ], + "name": "OnlyConfigOperatorOrOwner", + "type": "error" + }, { "anonymous": false, "inputs": [ diff --git a/lit-api-server/src/accounts/contracts/account_config_contract.rs b/lit-api-server/src/accounts/contracts/account_config_contract.rs index d55f1cef..a34f99fc 100644 --- a/lit-api-server/src/accounts/contracts/account_config_contract.rs +++ b/lit-api-server/src/accounts/contracts/account_config_contract.rs @@ -1642,7 +1642,6 @@ interface AccountConfig { event GroupRemoved(uint256 indexed apiKeyHash, uint256 indexed groupId); event GroupUpdated(uint256 indexed accountApiKeyHash, uint256 indexed groupId); event PkpAddedToGroup(uint256 indexed apiKeyHash, uint256 indexed groupId, address pkpId); - event PkpOwnerBackfilled(address indexed pkpId, uint256 indexed masterHash); event PkpRemovedFromGroup(uint256 indexed apiKeyHash, uint256 indexed groupId, address pkpId); event PricingOperatorUpdated(address indexed newPricingOperator); event PricingUpdated(uint256 indexed pricingItemId, uint256 price); @@ -1665,7 +1664,6 @@ interface AccountConfig { function apiKeyCanExecuteForAnyGroup(uint256 apiKeyHash, uint256[] memory groupIds) external view returns (bool); function apiPayerCount() external view returns (uint256); function api_payers() external view returns (address[] memory); - function backfillPkpOwners(address[] memory pkpIds, uint256[] memory masterHashes) external; function canExecuteAction(uint256 apiKeyHash, uint256 cidHash) external view returns (bool); function canExecuteActionAndUseWallet(uint256 apiKeyHash, uint256 cidHash, address walletAddress) external view returns (bool canExecute, bool canUseWallet); function canExecuteActionFast(uint256 apiKeyHash, uint256 cidHash) external view returns (bool); @@ -1955,24 +1953,6 @@ interface AccountConfig { ], "stateMutability": "view" }, - { - "type": "function", - "name": "backfillPkpOwners", - "inputs": [ - { - "name": "pkpIds", - "type": "address[]", - "internalType": "address[]" - }, - { - "name": "masterHashes", - "type": "uint256[]", - "internalType": "uint256[]" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, { "type": "function", "name": "canExecuteAction", @@ -3754,25 +3734,6 @@ interface AccountConfig { ], "anonymous": false }, - { - "type": "event", - "name": "PkpOwnerBackfilled", - "inputs": [ - { - "name": "pkpId", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "masterHash", - "type": "uint256", - "indexed": true, - "internalType": "uint256" - } - ], - "anonymous": false - }, { "type": "event", "name": "PkpRemovedFromGroup", @@ -7762,120 +7723,6 @@ pub mod AccountConfig { } }; #[derive(serde::Serialize, serde::Deserialize, Default, Debug, PartialEq, Eq, Hash)] - /*Event with signature `PkpOwnerBackfilled(address,uint256)` and selector `0x30af16efa92e1e313413b3b542aaad6085038078dfc320d3b4436ace4d48b65b`. - ```solidity - event PkpOwnerBackfilled(address indexed pkpId, uint256 indexed masterHash); - ```*/ - #[allow( - non_camel_case_types, - non_snake_case, - clippy::pub_underscore_fields, - clippy::style - )] - #[derive(Clone)] - pub struct PkpOwnerBackfilled { - #[allow(missing_docs)] - pub pkpId: alloy::sol_types::private::Address, - #[allow(missing_docs)] - pub masterHash: alloy::sol_types::private::primitives::aliases::U256, - } - #[allow( - non_camel_case_types, - non_snake_case, - clippy::pub_underscore_fields, - clippy::style - )] - const _: () = { - use alloy::sol_types as alloy_sol_types; - #[automatically_derived] - impl alloy_sol_types::SolEvent for PkpOwnerBackfilled { - type DataTuple<'a> = (); - type DataToken<'a> = as alloy_sol_types::SolType>::Token<'a>; - type TopicList = ( - alloy_sol_types::sol_data::FixedBytes<32>, - alloy::sol_types::sol_data::Address, - alloy::sol_types::sol_data::Uint<256>, - ); - const SIGNATURE: &'static str = "PkpOwnerBackfilled(address,uint256)"; - const SIGNATURE_HASH: alloy_sol_types::private::B256 = - alloy_sol_types::private::B256::new([ - 48u8, 175u8, 22u8, 239u8, 169u8, 46u8, 30u8, 49u8, 52u8, 19u8, 179u8, 181u8, - 66u8, 170u8, 173u8, 96u8, 133u8, 3u8, 128u8, 120u8, 223u8, 195u8, 32u8, 211u8, - 180u8, 67u8, 106u8, 206u8, 77u8, 72u8, 182u8, 91u8, - ]); - const ANONYMOUS: bool = false; - #[allow(unused_variables)] - #[inline] - fn new( - topics: ::RustType, - data: as alloy_sol_types::SolType>::RustType, - ) -> Self { - Self { - pkpId: topics.1, - masterHash: topics.2, - } - } - #[inline] - fn check_signature( - topics: &::RustType, - ) -> alloy_sol_types::Result<()> { - if topics.0 != Self::SIGNATURE_HASH { - return Err(alloy_sol_types::Error::invalid_event_signature_hash( - Self::SIGNATURE, - topics.0, - Self::SIGNATURE_HASH, - )); - } - Ok(()) - } - #[inline] - fn tokenize_body(&self) -> Self::DataToken<'_> { - () - } - #[inline] - fn topics(&self) -> ::RustType { - ( - Self::SIGNATURE_HASH.into(), - self.pkpId.clone(), - self.masterHash.clone(), - ) - } - #[inline] - fn encode_topics_raw( - &self, - out: &mut [alloy_sol_types::abi::token::WordToken], - ) -> alloy_sol_types::Result<()> { - if out.len() < ::COUNT { - return Err(alloy_sol_types::Error::Overrun); - } - out[0usize] = alloy_sol_types::abi::token::WordToken(Self::SIGNATURE_HASH); - out[1usize] = ::encode_topic( - &self.pkpId, - ); - out[2usize] = as alloy_sol_types::EventTopic>::encode_topic(&self.masterHash); - Ok(()) - } - } - #[automatically_derived] - impl alloy_sol_types::private::IntoLogData for PkpOwnerBackfilled { - fn to_log_data(&self) -> alloy_sol_types::private::LogData { - From::from(self) - } - fn into_log_data(self) -> alloy_sol_types::private::LogData { - From::from(&self) - } - } - #[automatically_derived] - impl From<&PkpOwnerBackfilled> for alloy_sol_types::private::LogData { - #[inline] - fn from(this: &PkpOwnerBackfilled) -> alloy_sol_types::private::LogData { - alloy_sol_types::SolEvent::encode_log_data(this) - } - } - }; - #[derive(serde::Serialize, serde::Deserialize, Default, Debug, PartialEq, Eq, Hash)] /*Event with signature `PkpRemovedFromGroup(uint256,uint256,address)` and selector `0x071e211a142d40078c2724eb4fc9fc3bd257912c5a496497605f420355521145`. ```solidity event PkpRemovedFromGroup(uint256 indexed apiKeyHash, uint256 indexed groupId, address pkpId); @@ -10618,157 +10465,6 @@ pub mod AccountConfig { } }; #[derive(serde::Serialize, serde::Deserialize, Default, Debug, PartialEq, Eq, Hash)] - /*Function with signature `backfillPkpOwners(address[],uint256[])` and selector `0x41275609`. - ```solidity - function backfillPkpOwners(address[] memory pkpIds, uint256[] memory masterHashes) external; - ```*/ - #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)] - #[derive(Clone)] - pub struct backfillPkpOwnersCall { - #[allow(missing_docs)] - pub pkpIds: alloy::sol_types::private::Vec, - #[allow(missing_docs)] - pub masterHashes: - alloy::sol_types::private::Vec, - } - //Container type for the return parameters of the [`backfillPkpOwners(address[],uint256[])`](backfillPkpOwnersCall) function. - #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields)] - #[derive(Clone)] - pub struct backfillPkpOwnersReturn {} - #[allow( - non_camel_case_types, - non_snake_case, - clippy::pub_underscore_fields, - clippy::style - )] - const _: () = { - use alloy::sol_types as alloy_sol_types; - { - #[doc(hidden)] - #[allow(dead_code)] - type UnderlyingSolTuple<'a> = ( - alloy::sol_types::sol_data::Array, - alloy::sol_types::sol_data::Array>, - ); - #[doc(hidden)] - type UnderlyingRustTuple<'a> = ( - alloy::sol_types::private::Vec, - alloy::sol_types::private::Vec< - alloy::sol_types::private::primitives::aliases::U256, - >, - ); - #[cfg(test)] - #[allow(dead_code, unreachable_patterns)] - fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { - match _t { - alloy_sol_types::private::AssertTypeEq::< - ::RustType, - >(_) => {} - } - } - #[automatically_derived] - #[doc(hidden)] - impl ::core::convert::From for UnderlyingRustTuple<'_> { - fn from(value: backfillPkpOwnersCall) -> Self { - (value.pkpIds, value.masterHashes) - } - } - #[automatically_derived] - #[doc(hidden)] - impl ::core::convert::From> for backfillPkpOwnersCall { - fn from(tuple: UnderlyingRustTuple<'_>) -> Self { - Self { - pkpIds: tuple.0, - masterHashes: tuple.1, - } - } - } - } - { - #[doc(hidden)] - #[allow(dead_code)] - type UnderlyingSolTuple<'a> = (); - #[doc(hidden)] - type UnderlyingRustTuple<'a> = (); - #[cfg(test)] - #[allow(dead_code, unreachable_patterns)] - fn _type_assertion(_t: alloy_sol_types::private::AssertTypeEq) { - match _t { - alloy_sol_types::private::AssertTypeEq::< - ::RustType, - >(_) => {} - } - } - #[automatically_derived] - #[doc(hidden)] - impl ::core::convert::From for UnderlyingRustTuple<'_> { - fn from(value: backfillPkpOwnersReturn) -> Self { - () - } - } - #[automatically_derived] - #[doc(hidden)] - impl ::core::convert::From> for backfillPkpOwnersReturn { - fn from(tuple: UnderlyingRustTuple<'_>) -> Self { - Self {} - } - } - } - impl backfillPkpOwnersReturn { - fn _tokenize( - &self, - ) -> ::ReturnToken<'_> { - () - } - } - #[automatically_derived] - impl alloy_sol_types::SolCall for backfillPkpOwnersCall { - type Parameters<'a> = ( - alloy::sol_types::sol_data::Array, - alloy::sol_types::sol_data::Array>, - ); - type Token<'a> = as alloy_sol_types::SolType>::Token<'a>; - type Return = backfillPkpOwnersReturn; - type ReturnTuple<'a> = (); - type ReturnToken<'a> = as alloy_sol_types::SolType>::Token<'a>; - const SIGNATURE: &'static str = "backfillPkpOwners(address[],uint256[])"; - const SELECTOR: [u8; 4] = [65u8, 39u8, 86u8, 9u8]; - #[inline] - fn new<'a>( - tuple: as alloy_sol_types::SolType>::RustType, - ) -> Self { - tuple.into() - } - #[inline] - fn tokenize(&self) -> Self::Token<'_> { - ( - as alloy_sol_types::SolType>::tokenize(&self.pkpIds), - , - > as alloy_sol_types::SolType>::tokenize(&self.masterHashes), - ) - } - #[inline] - fn tokenize_returns(ret: &Self::Return) -> Self::ReturnToken<'_> { - backfillPkpOwnersReturn::_tokenize(ret) - } - #[inline] - fn abi_decode_returns(data: &[u8]) -> alloy_sol_types::Result { - as alloy_sol_types::SolType>::abi_decode_sequence(data) - .map(Into::into) - } - #[inline] - fn abi_decode_returns_validate(data: &[u8]) -> alloy_sol_types::Result { - as alloy_sol_types::SolType>::abi_decode_sequence_validate( - data, - ) - .map(Into::into) - } - } - }; - #[derive(serde::Serialize, serde::Deserialize, Default, Debug, PartialEq, Eq, Hash)] /*Function with signature `canExecuteAction(uint256,uint256)` and selector `0xff1fe00c`. ```solidity function canExecuteAction(uint256 apiKeyHash, uint256 cidHash) external view returns (bool); @@ -19449,8 +19145,6 @@ pub mod AccountConfig { #[allow(missing_docs)] api_payers(api_payersCall), #[allow(missing_docs)] - backfillPkpOwners(backfillPkpOwnersCall), - #[allow(missing_docs)] canExecuteAction(canExecuteActionCall), #[allow(missing_docs)] canExecuteActionAndUseWallet(canExecuteActionAndUseWalletCall), @@ -19590,7 +19284,6 @@ pub mod AccountConfig { [56u8, 54u8, 3u8, 254u8], [64u8, 180u8, 212u8, 83u8], [64u8, 212u8, 65u8, 27u8], - [65u8, 39u8, 86u8, 9u8], [74u8, 66u8, 196u8, 10u8], [77u8, 190u8, 191u8, 229u8], [80u8, 64u8, 144u8, 1u8], @@ -19661,7 +19354,6 @@ pub mod AccountConfig { ::core::stringify!(adminApiPayerAccount), ::core::stringify!(debitApiKey), ::core::stringify!(removeAction), - ::core::stringify!(backfillPkpOwners), ::core::stringify!(removePkpFromGroup), ::core::stringify!(newChainSecuredAccount), ::core::stringify!(listPkps), @@ -19732,7 +19424,6 @@ pub mod AccountConfig { ::SIGNATURE, ::SIGNATURE, ::SIGNATURE, - ::SIGNATURE, ::SIGNATURE, ::SIGNATURE, ::SIGNATURE, @@ -19805,7 +19496,7 @@ pub mod AccountConfig { impl alloy_sol_types::SolInterface for AccountConfigCalls { const NAME: &'static str = "AccountConfigCalls"; const MIN_DATA_LENGTH: usize = 0usize; - const COUNT: usize = 68usize; + const COUNT: usize = 67usize; #[inline] fn selector(&self) -> [u8; 4] { match self { @@ -19828,9 +19519,6 @@ pub mod AccountConfig { } Self::apiPayerCount(_) => ::SELECTOR, Self::api_payers(_) => ::SELECTOR, - Self::backfillPkpOwners(_) => { - ::SELECTOR - } Self::canExecuteAction(_) => { ::SELECTOR } @@ -20146,15 +19834,6 @@ pub mod AccountConfig { } removeAction }, - { - fn backfillPkpOwners( - data: &[u8], - ) -> alloy_sol_types::Result { - ::abi_decode_raw(data) - .map(AccountConfigCalls::backfillPkpOwners) - } - backfillPkpOwners - }, { fn removePkpFromGroup( data: &[u8], @@ -20785,17 +20464,6 @@ pub mod AccountConfig { } removeAction }, - { - fn backfillPkpOwners( - data: &[u8], - ) -> alloy_sol_types::Result { - ::abi_decode_raw_validate( - data, - ) - .map(AccountConfigCalls::backfillPkpOwners) - } - backfillPkpOwners - }, { fn removePkpFromGroup( data: &[u8], @@ -21325,11 +20993,6 @@ pub mod AccountConfig { Self::api_payers(inner) => { ::abi_encoded_size(inner) } - Self::backfillPkpOwners(inner) => { - ::abi_encoded_size( - inner, - ) - } Self::canExecuteAction(inner) => { ::abi_encoded_size( inner, @@ -21667,12 +21330,6 @@ pub mod AccountConfig { out, ) } - Self::backfillPkpOwners(inner) => { - ::abi_encode_raw( - inner, - out, - ) - } Self::canExecuteAction(inner) => { ::abi_encode_raw( inner, @@ -22885,8 +22542,6 @@ pub mod AccountConfig { #[allow(missing_docs)] PkpAddedToGroup(PkpAddedToGroup), #[allow(missing_docs)] - PkpOwnerBackfilled(PkpOwnerBackfilled), - #[allow(missing_docs)] PkpRemovedFromGroup(PkpRemovedFromGroup), #[allow(missing_docs)] PricingOperatorUpdated(PricingOperatorUpdated), @@ -22980,11 +22635,6 @@ pub mod AccountConfig { 188u8, 176u8, 95u8, 160u8, 2u8, 71u8, 184u8, 248u8, 24u8, 40u8, 236u8, 4u8, 201u8, 119u8, 55u8, 139u8, 233u8, 237u8, 223u8, 93u8, ], - [ - 48u8, 175u8, 22u8, 239u8, 169u8, 46u8, 30u8, 49u8, 52u8, 19u8, 179u8, 181u8, 66u8, - 170u8, 173u8, 96u8, 133u8, 3u8, 128u8, 120u8, 223u8, 195u8, 32u8, 211u8, 180u8, - 67u8, 106u8, 206u8, 77u8, 72u8, 182u8, 91u8, - ], [ 51u8, 95u8, 90u8, 252u8, 131u8, 254u8, 140u8, 90u8, 1u8, 26u8, 150u8, 220u8, 57u8, 188u8, 206u8, 159u8, 185u8, 212u8, 111u8, 181u8, 152u8, 101u8, 2u8, 247u8, 4u8, @@ -23066,7 +22716,6 @@ pub mod AccountConfig { ::core::stringify!(WalletDerivationRegistered), ::core::stringify!(ConfigOperatorUpdated), ::core::stringify!(GroupRemoved), - ::core::stringify!(PkpOwnerBackfilled), ::core::stringify!(PricingUpdated), ::core::stringify!(WalletDerivationRemoved), ::core::stringify!(GroupUpdated), @@ -23096,7 +22745,6 @@ pub mod AccountConfig { ::SIGNATURE, ::SIGNATURE, ::SIGNATURE, - ::SIGNATURE, ::SIGNATURE, ::SIGNATURE, ::SIGNATURE, @@ -23133,7 +22781,7 @@ pub mod AccountConfig { #[automatically_derived] impl alloy_sol_types::SolEventInterface for AccountConfigEvents { const NAME: &'static str = "AccountConfigEvents"; - const COUNT: usize = 27usize; + const COUNT: usize = 26usize; fn decode_raw_log( topics: &[alloy_sol_types::Word], data: &[u8], @@ -23263,15 +22911,6 @@ pub mod AccountConfig { ) .map(Self::PkpAddedToGroup) } - Some( - ::SIGNATURE_HASH, - ) => { - ::decode_raw_log( - topics, - data, - ) - .map(Self::PkpOwnerBackfilled) - } Some( ::SIGNATURE_HASH, ) => { @@ -23422,9 +23061,6 @@ pub mod AccountConfig { Self::PkpAddedToGroup(inner) => { alloy_sol_types::private::IntoLogData::to_log_data(inner) } - Self::PkpOwnerBackfilled(inner) => { - alloy_sol_types::private::IntoLogData::to_log_data(inner) - } Self::PkpRemovedFromGroup(inner) => { alloy_sol_types::private::IntoLogData::to_log_data(inner) } @@ -23507,9 +23143,6 @@ pub mod AccountConfig { Self::PkpAddedToGroup(inner) => { alloy_sol_types::private::IntoLogData::into_log_data(inner) } - Self::PkpOwnerBackfilled(inner) => { - alloy_sol_types::private::IntoLogData::into_log_data(inner) - } Self::PkpRemovedFromGroup(inner) => { alloy_sol_types::private::IntoLogData::into_log_data(inner) } @@ -23749,19 +23382,6 @@ pub mod AccountConfig { pub fn api_payers(&self) -> alloy_contract::SolCallBuilder<&P, api_payersCall, N> { self.call_builder(&api_payersCall) } - //Creates a new call builder for the [`backfillPkpOwners`] function. - pub fn backfillPkpOwners( - &self, - pkpIds: alloy::sol_types::private::Vec, - masterHashes: alloy::sol_types::private::Vec< - alloy::sol_types::private::primitives::aliases::U256, - >, - ) -> alloy_contract::SolCallBuilder<&P, backfillPkpOwnersCall, N> { - self.call_builder(&backfillPkpOwnersCall { - pkpIds, - masterHashes, - }) - } //Creates a new call builder for the [`canExecuteAction`] function. pub fn canExecuteAction( &self, @@ -24460,12 +24080,6 @@ pub mod AccountConfig { pub fn PkpAddedToGroup_filter(&self) -> alloy_contract::Event<&P, PkpAddedToGroup, N> { self.event_filter::() } - //Creates a new event filter for the [`PkpOwnerBackfilled`] event. - pub fn PkpOwnerBackfilled_filter( - &self, - ) -> alloy_contract::Event<&P, PkpOwnerBackfilled, N> { - self.event_filter::() - } //Creates a new event filter for the [`PkpRemovedFromGroup`] event. pub fn PkpRemovedFromGroup_filter( &self,