diff --git a/README.md b/README.md index b10db2e..b2818c9 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Check out the full documentation in [`/docs`](./docs)! - [Assertions](./docs/api-reference/assertions.md) - Static & fluent assertion API - [Blockchain API](./docs/api-reference/blockchain.md) - Blockchain simulator - [Contract Runtime](./docs/api-reference/contract-runtime.md) - Custom contract wrappers -- [Advanced Topics](./docs/advanced/cross-contract-calls.md) - Upgrades, signatures, gas profiling, consensus rules +- [Advanced Topics](./docs/advanced/cross-contract-calls.md) - Updates, signatures, gas profiling, consensus rules - [Examples](./docs/examples/nativeswap-testing.md) - Real-world test examples - [API Reference](./docs/api-reference/types-interfaces.md) - Full type reference diff --git a/SECURITY.md b/SECURITY.md index 671d122..4866c9f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -30,7 +30,7 @@ This policy covers: - The OP_VM integration layer (`ContractRuntime`, `RustContract`) - State management (`StateHandler`, `BytecodeManager`) - Gas accounting and consensus rule enforcement -- Contract upgrade mechanisms (`updateFromAddress`, `applyPendingBytecodeUpgrade`) +- Contract update mechanisms (`updateFromAddress`, `applyPendingBytecodeUpdate`) ## Out of Scope diff --git a/docs/README.md b/docs/README.md index 4e38b43..ae1ccd0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -25,7 +25,7 @@ Welcome to the documentation for `@btc-vision/unit-test-framework`. This framewo ### Advanced Topics - [Cross-Contract Calls](./advanced/cross-contract-calls.md) - Multi-contract testing -- [Upgradeable Contracts](./advanced/upgradeable-contracts.md) - Testing contract upgrades +- [Updatable Contracts](./advanced/updatable-contracts.md) - Testing contract updates - [Transaction Simulation](./advanced/transaction-simulation.md) - Bitcoin transaction inputs/outputs - [Signature Verification](./advanced/signature-verification.md) - ML-DSA, Schnorr, ECDSA - [State Management](./advanced/state-management.md) - State overrides and block replay diff --git a/docs/advanced/consensus-rules.md b/docs/advanced/consensus-rules.md index fae6193..779d38a 100644 --- a/docs/advanced/consensus-rules.md +++ b/docs/advanced/consensus-rules.md @@ -18,7 +18,7 @@ import { ConsensusRules, ConsensusManager } from '@btc-vision/unit-test-framewor |------|-------|-------------| | `NONE` | `0b00000000` | No flags | | `ALLOW_CLASSICAL_SIGNATURES` | `0b00000001` | Allow Schnorr/ECDSA (non-quantum) | -| `UPDATE_CONTRACT_BY_ADDRESS` | `0b00000010` | Allow contract upgrades | +| `UPDATE_CONTRACT_BY_ADDRESS` | `0b00000010` | Allow contract updates | | `RESERVED_FLAG_2` | `0b00000100` | Reserved for future use | ### Creating Rules diff --git a/docs/advanced/cross-contract-calls.md b/docs/advanced/cross-contract-calls.md index ba327b3..0e29b62 100644 --- a/docs/advanced/cross-contract-calls.md +++ b/docs/advanced/cross-contract-calls.md @@ -168,4 +168,4 @@ await vm.it('should compare two scenarios', async () => { --- -[<- Previous: Utilities](../api-reference/utilities.md) | [Next: Upgradeable Contracts ->](./upgradeable-contracts.md) +[<- Previous: Utilities](../api-reference/utilities.md) | [Next: Updatable Contracts ->](./updatable-contracts.md) diff --git a/docs/advanced/transaction-simulation.md b/docs/advanced/transaction-simulation.md index ee240f8..9a79e15 100644 --- a/docs/advanced/transaction-simulation.md +++ b/docs/advanced/transaction-simulation.md @@ -116,4 +116,4 @@ This is handled automatically by the framework when `Blockchain.transaction` is --- -[<- Previous: Upgradeable Contracts](./upgradeable-contracts.md) | [Next: Signature Verification ->](./signature-verification.md) +[<- Previous: Updatable Contracts](./updatable-contracts.md) | [Next: Signature Verification ->](./signature-verification.md) diff --git a/docs/advanced/upgradeable-contracts.md b/docs/advanced/updatable-contracts.md similarity index 68% rename from docs/advanced/upgradeable-contracts.md rename to docs/advanced/updatable-contracts.md index 2eedf0a..02c6fa0 100644 --- a/docs/advanced/upgradeable-contracts.md +++ b/docs/advanced/updatable-contracts.md @@ -1,10 +1,10 @@ -# Upgradeable Contracts +# Updatable Contracts -OPNet supports contract upgrades via `updateFromAddress`. The new bytecode is sourced from another registered contract and takes effect on the next block. +OPNet supports contract updates via `updateFromAddress`. The new bytecode is sourced from another registered contract and takes effect on the next block. --- -## How Upgrades Work +## How Updates Work ```mermaid sequenceDiagram @@ -13,8 +13,8 @@ sequenceDiagram participant V2Source as V2 Source participant BC as Blockchain - Test->>V1: upgrade(v2SourceAddress) - Note over V1: Upgrade queued + Test->>V1: update(v2SourceAddress) + Note over V1: Update queued Test->>BC: mineBlock() Note over V1: Bytecode replaced with V2 Test->>V1: getValue() @@ -22,37 +22,37 @@ sequenceDiagram ``` Key points: -- Upgrades are **queued**, not immediate +- Updates are **queued**, not immediate - The new bytecode takes effect after `Blockchain.mineBlock()` -- Storage is **preserved** across upgrades -- Only **one upgrade per block** is allowed +- Storage is **preserved** across updates +- Only **one update per block** is allowed - The contract **address stays the same** -- Cross-contract calls are **blocked** after upgrading in the same execution +- Cross-contract calls are **blocked** after updating in the same execution --- ## Internal Mechanism -The upgrade process has two phases: +The update process has two phases: -### Phase 1: Upgrade Request (`updateFromAddress`) +### Phase 1: Update Request (`updateFromAddress`) When a contract calls `updateFromAddress`: 1. A **temporary WASM instance** is created with the **current** bytecode (using `bypassCache: true` to avoid reusing the paused instance) 2. `onUpdate(calldata)` is called on this temporary instance, giving the current contract a chance to run migration logic -3. If `onUpdate` succeeds, the new bytecode is **queued** as a pending upgrade for the current block -4. The `_hasUpgradedInCurrentExecution` flag is set, **blocking** any further cross-contract calls (`Blockchain.call`) in the same transaction +3. If `onUpdate` succeeds, the new bytecode is **queued** as a pending update for the current block +4. The `_hasUpdatedInCurrentExecution` flag is set, **blocking** any further cross-contract calls (`Blockchain.call`) in the same transaction -### Phase 2: Bytecode Swap (`applyPendingBytecodeUpgrade`) +### Phase 2: Bytecode Swap (`applyPendingBytecodeUpdate`) On the **next block** (when `Blockchain.blockNumber > pendingBytecodeBlock`): 1. The bytecode is swapped to the new version 2. A **temporary WASM instance** is created with the **new** bytecode (using `bypassCache: true` to ensure the fresh bytecode is loaded without hitting the module cache) 3. `onUpdate(calldata)` is called on the new bytecode, allowing the new version to run its own initialization/migration logic -4. If `onUpdate` fails on the new bytecode, the **upgrade is reverted** back to the previous bytecode -5. The pending upgrade state is cleared +4. If `onUpdate` fails on the new bytecode, the **update is reverted** back to the previous bytecode +5. The pending update state is cleared ### Response Format @@ -67,7 +67,7 @@ You need two contracts: the main contract and a source contract that holds the V ```typescript import { Address } from '@btc-vision/transaction'; import { BytecodeManager, ContractRuntime, opnet, OPNetUnit, Assert, Blockchain } from '@btc-vision/unit-test-framework'; -import { UpgradeableContractRuntime } from './UpgradeableContractRuntime.js'; +import { UpdatableContractRuntime } from './UpdatableContractRuntime.js'; // Source contract just provides bytecode class V2SourceContract extends ContractRuntime { @@ -89,11 +89,11 @@ class V2SourceContract extends ContractRuntime { ## Test Examples -### Basic Upgrade +### Basic Update ```typescript -await opnet('Upgrade Tests', async (vm: OPNetUnit) => { - let contract: UpgradeableContractRuntime; +await opnet('Update Tests', async (vm: OPNetUnit) => { + let contract: UpdatableContractRuntime; let v2Source: V2SourceContract; const deployer = Blockchain.generateRandomAddress(); @@ -105,7 +105,7 @@ await opnet('Upgrade Tests', async (vm: OPNetUnit) => { Blockchain.clearContracts(); await Blockchain.init(); - contract = new UpgradeableContractRuntime(deployer, contractAddress); + contract = new UpdatableContractRuntime(deployer, contractAddress); v2Source = new V2SourceContract(deployer, v2Address); Blockchain.register(contract); @@ -124,17 +124,17 @@ await opnet('Upgrade Tests', async (vm: OPNetUnit) => { Blockchain.dispose(); }); - await vm.it('should not apply upgrade on same block', async () => { + await vm.it('should not apply update on same block', async () => { Assert.expect(await contract.getValue()).toEqual(1); - await contract.upgrade(v2Address); + await contract.update(v2Address); // Same block: still V1 behavior Assert.expect(await contract.getValue()).toEqual(1); }); - await vm.it('should apply upgrade after mining', async () => { - await contract.upgrade(v2Address); + await vm.it('should apply update after mining', async () => { + await contract.update(v2Address); Blockchain.mineBlock(); // Now V2 behavior @@ -146,7 +146,7 @@ await opnet('Upgrade Tests', async (vm: OPNetUnit) => { ### Storage Persistence ```typescript -await vm.it('should preserve storage across upgrade', async () => { +await vm.it('should preserve storage across update', async () => { const key = new Uint8Array(32); key[31] = 42; const value = new Uint8Array(32); @@ -155,8 +155,8 @@ await vm.it('should preserve storage across upgrade', async () => { // Store value with V1 await contract.storeValue(key, value); - // Upgrade to V2 - await contract.upgrade(v2Address); + // Update to V2 + await contract.update(v2Address); Blockchain.mineBlock(); // Value persists with V2 @@ -172,15 +172,15 @@ await vm.it('should revert for non-existent source', async () => { const fakeAddress = Blockchain.generateRandomAddress(); await Assert.expect(async () => { - await contract.upgrade(fakeAddress); + await contract.update(fakeAddress); }).toThrow(); }); -await vm.it('should reject second upgrade in same block', async () => { - await contract.upgrade(v2Address); +await vm.it('should reject second update in same block', async () => { + await contract.update(v2Address); await Assert.expect(async () => { - await contract.upgrade(v2Address); + await contract.update(v2Address); }).toThrow(); }); ``` @@ -188,10 +188,10 @@ await vm.it('should reject second upgrade in same block', async () => { ### Gas Tracking ```typescript -await vm.it('should measure upgrade gas cost', async () => { - const response = await contract.upgrade(v2Address); +await vm.it('should measure update gas cost', async () => { + const response = await contract.update(v2Address); Assert.expect(response.usedGas).toBeGreaterThan(0n); - vm.info(`Upgrade gas: ${response.usedGas}`); + vm.info(`Update gas: ${response.usedGas}`); }); ``` diff --git a/docs/api-reference/blockchain.md b/docs/api-reference/blockchain.md index f5bb108..e9ba377 100644 --- a/docs/api-reference/blockchain.md +++ b/docs/api-reference/blockchain.md @@ -68,12 +68,12 @@ Median block timestamp. mineBlock(): void ``` -Advances `blockNumber` by 1. Used to test block-boundary behavior like upgrades: +Advances `blockNumber` by 1. Used to test block-boundary behavior like updates: ```typescript -await contract.upgrade(v2Address); +await contract.update(v2Address); Blockchain.mineBlock(); -// Upgrade now takes effect +// Update now takes effect const value = await contract.getValue(); ``` diff --git a/docs/writing-tests/custom-contracts.md b/docs/writing-tests/custom-contracts.md index e7cfeba..9054e58 100644 --- a/docs/writing-tests/custom-contracts.md +++ b/docs/writing-tests/custom-contracts.md @@ -482,7 +482,7 @@ await opnet('TestContract', async (vm: OPNetUnit) => { - [Contract Runtime API Reference](../api-reference/contract-runtime.md) - Full API details - [Cross-Contract Calls](../advanced/cross-contract-calls.md) - Multi-contract testing -- [Upgradeable Contracts](../advanced/upgradeable-contracts.md) - Testing upgrades +- [Updatable Contracts](../advanced/updatable-contracts.md) - Testing updates --- diff --git a/scripts/buildTestContracts.js b/scripts/buildTestContracts.js index fd1e100..5fd41f3 100644 --- a/scripts/buildTestContracts.js +++ b/scripts/buildTestContracts.js @@ -7,10 +7,10 @@ execSync('asc index.ts --target debug --measure --uncheckedBehavior never'); process.chdir(`${root}/test/e2e/contracts/gas-test-contract/contract`); execSync('asc GasTestContract.ts --target debug --measure --uncheckedBehavior never'); -process.chdir(`${root}/test/e2e/contracts/upgradeable-contract/contract`); +process.chdir(`${root}/test/e2e/contracts/updatable-contract/contract`); execSync('asc index.ts --target debug --measure --uncheckedBehavior never'); -process.chdir(`${root}/test/e2e/contracts/upgradeable-contract-v2/contract`); +process.chdir(`${root}/test/e2e/contracts/updatable-contract-v2/contract`); execSync('asc index.ts --target debug --measure --uncheckedBehavior never'); process.chdir(`${root}/test/e2e/contracts/malicious-v2/contract`); diff --git a/src/opnet/modules/ContractRuntime.ts b/src/opnet/modules/ContractRuntime.ts index 789d4e7..cd4e011 100644 --- a/src/opnet/modules/ContractRuntime.ts +++ b/src/opnet/modules/ContractRuntime.ts @@ -84,8 +84,8 @@ export class ContractRuntime extends Logger { private readonly proofFeatureEnabled = false; private _pendingBytecode: Buffer | undefined; private _pendingBytecodeBlock: bigint | undefined; - private _pendingUpgradeCalldata: Buffer | undefined; - private _hasUpgradedInCurrentExecution: boolean = false; + private _pendingUpdateCalldata: Buffer | undefined; + private _hasUpdatedInCurrentExecution: boolean = false; protected constructor(details: ContractDetails) { super(); @@ -370,8 +370,8 @@ export class ContractRuntime extends Logger { StateHandler.pushAllTempStatesToGlobal(); } } else { - // Transaction reverted: cancel any pending bytecode upgrade - this.cancelPendingBytecodeUpgrade(); + // Transaction reverted: cancel any pending bytecode update + this.cancelPendingBytecodeUpdate(); } // Reset internal states @@ -401,12 +401,12 @@ export class ContractRuntime extends Logger { this.touchedAddresses = new AddressSet([this.address]); this.touchedBlocks = new Set([Blockchain.blockNumber]); - this._hasUpgradedInCurrentExecution = false; + this._hasUpdatedInCurrentExecution = false; } protected async executeCall(executionParameters: ExecutionParameters): Promise { - // Apply pending bytecode upgrade if block has advanced - const phase2Gas = await this.applyPendingBytecodeUpgrade(); + // Apply pending bytecode update if block has advanced + const phase2Gas = await this.applyPendingBytecodeUpdate(); // Deploy if not deployed. const deployment = await this.deployContract(false); @@ -558,7 +558,7 @@ export class ContractRuntime extends Logger { return result; } - private async applyPendingBytecodeUpgrade(): Promise { + private async applyPendingBytecodeUpdate(): Promise { if ( !this._pendingBytecode || this._pendingBytecodeBlock === undefined || @@ -569,7 +569,7 @@ export class ContractRuntime extends Logger { const previousBytecode = this._bytecode; const previousContract = this._contract; - const calldata = this._pendingUpgradeCalldata || Buffer.alloc(0); + const calldata = this._pendingUpdateCalldata || Buffer.alloc(0); // Swap to the new bytecode first this._bytecode = this._pendingBytecode; @@ -580,8 +580,8 @@ export class ContractRuntime extends Logger { let tempContract: RustContract | undefined; let phase2GasUsed: bigint = 0n; try { - // Block cross-contract calls and nested upgrades during Phase 2 onUpdate - this._hasUpgradedInCurrentExecution = true; + // Block cross-contract calls and nested updates during Phase 2 onUpdate + this._hasUpdatedInCurrentExecution = true; tempContract = new RustContract(this.generateParams(true)); this._contract = tempContract; @@ -599,7 +599,7 @@ export class ContractRuntime extends Logger { // onUpdate failed — revert bytecode swap if (!previousBytecode) { throw new Error( - 'OP_NET: FATAL — upgrade revert with no previous bytecode (impossible state)', + 'OP_NET: FATAL — update revert with no previous bytecode (impossible state)', { cause: e }, ); } @@ -626,12 +626,12 @@ export class ContractRuntime extends Logger { } if (this.logUnexpectedErrors) { - this.warn(`onUpdate failed, upgrade reverted: ${(e as Error).message}`); + this.warn(`onUpdate failed, update reverted: ${(e as Error).message}`); } } finally { // Always restore original contract reference this._contract = previousContract; - this._hasUpgradedInCurrentExecution = false; + this._hasUpdatedInCurrentExecution = false; if (tempContract) { try { @@ -643,16 +643,16 @@ export class ContractRuntime extends Logger { this._pendingBytecode = undefined; this._pendingBytecodeBlock = undefined; - this._pendingUpgradeCalldata = undefined; + this._pendingUpdateCalldata = undefined; } return phase2GasUsed; } - private cancelPendingBytecodeUpgrade(): void { + private cancelPendingBytecodeUpdate(): void { this._pendingBytecode = undefined; this._pendingBytecodeBlock = undefined; - this._pendingUpgradeCalldata = undefined; + this._pendingUpdateCalldata = undefined; } private getGasUsed(): bigint { @@ -668,8 +668,8 @@ export class ContractRuntime extends Logger { } private async updateFromAddress(data: Buffer): Promise { - if (this._hasUpgradedInCurrentExecution) { - throw new Error('OP_NET: Cannot upgrade while another upgrade is in progress'); + if (this._hasUpdatedInCurrentExecution) { + throw new Error('OP_NET: Cannot update while another update is in progress'); } try { @@ -680,16 +680,16 @@ export class ContractRuntime extends Logger { if (calldata.byteLength > CONSENSUS.COMPRESSION.MAX_DECOMPRESSED_SIZE) { throw new Error( - `OP_NET: Upgrade calldata exceeds maximum decompressed size (${calldata.byteLength} > ${CONSENSUS.COMPRESSION.MAX_DECOMPRESSED_SIZE})`, + `OP_NET: Update calldata exceeds maximum decompressed size (${calldata.byteLength} > ${CONSENSUS.COMPRESSION.MAX_DECOMPRESSED_SIZE})`, ); } - // Enforce one upgrade per block per contract + // Enforce one update per block per contract if ( this._pendingBytecodeBlock !== undefined && this._pendingBytecodeBlock === Blockchain.blockNumber ) { - throw new Error('OP_NET: Only one upgrade per block per contract is allowed'); + throw new Error('OP_NET: Only one update per block per contract is allowed'); } const gasBefore = this.gasUsed; @@ -701,11 +701,11 @@ export class ContractRuntime extends Logger { const originalContract = this._contract; let tempContract: RustContract | undefined; - // Set upgrade flag BEFORE onUpdate to prevent: + // Set update flag BEFORE onUpdate to prevent: // 1. Nested updateFromAddress calls from within onUpdate // 2. Cross-contract calls (Blockchain.call) from within onUpdate - this._hasUpgradedInCurrentExecution = true; - let upgradeCommitted = false; + this._hasUpdatedInCurrentExecution = true; + let updateCommitted = false; try { tempContract = new RustContract(this.generateParams(true)); @@ -736,8 +736,8 @@ export class ContractRuntime extends Logger { // Schedule bytecode replacement for the next block this._pendingBytecode = newBytecode; this._pendingBytecodeBlock = Blockchain.blockNumber; - this._pendingUpgradeCalldata = calldata; - upgradeCommitted = true; + this._pendingUpdateCalldata = calldata; + updateCommitted = true; const used = response.gasUsed - gasBefore; this.gasUsed = response.gasUsed; @@ -751,8 +751,8 @@ export class ContractRuntime extends Logger { } finally { this._contract = originalContract; - if (!upgradeCommitted) { - this._hasUpgradedInCurrentExecution = false; + if (!updateCommitted) { + this._hasUpdatedInCurrentExecution = false; } if (tempContract) { @@ -778,9 +778,9 @@ export class ContractRuntime extends Logger { } private async deployContractAtAddress(data: Buffer): Promise { - if (this._hasUpgradedInCurrentExecution) { + if (this._hasUpdatedInCurrentExecution) { throw new Error( - 'OP_NET: Cannot deploy contracts after upgrading bytecode in the same execution', + 'OP_NET: Cannot deploy contracts after updating bytecode in the same execution', ); } @@ -1032,9 +1032,9 @@ export class ContractRuntime extends Logger { throw new Error('Contract not initialized'); } - if (this._hasUpgradedInCurrentExecution) { + if (this._hasUpdatedInCurrentExecution) { throw new Error( - 'OP_NET: Cannot call other contracts after upgrading bytecode in the same execution', + 'OP_NET: Cannot call other contracts after updating bytecode in the same execution', ); } diff --git a/test/e2e/contracts/upgradeable-contract-v2/contract/UpgradeableContractV2.ts b/test/e2e/contracts/updatable-contract-v2/contract/UpdatableContractV2.ts similarity index 95% rename from test/e2e/contracts/upgradeable-contract-v2/contract/UpgradeableContractV2.ts rename to test/e2e/contracts/updatable-contract-v2/contract/UpdatableContractV2.ts index 33e4d8e..9c04dcc 100644 --- a/test/e2e/contracts/upgradeable-contract-v2/contract/UpgradeableContractV2.ts +++ b/test/e2e/contracts/updatable-contract-v2/contract/UpdatableContractV2.ts @@ -6,7 +6,7 @@ import { } from '@btc-vision/btc-runtime/runtime'; @final -export class UpgradeableContractV2 extends OP_NET { +export class UpdatableContractV2 extends OP_NET { public constructor() { super(); } diff --git a/test/e2e/contracts/upgradeable-contract/contract/asconfig.json b/test/e2e/contracts/updatable-contract-v2/contract/asconfig.json similarity index 84% rename from test/e2e/contracts/upgradeable-contract/contract/asconfig.json rename to test/e2e/contracts/updatable-contract-v2/contract/asconfig.json index 9ebc239..85e6855 100644 --- a/test/e2e/contracts/upgradeable-contract/contract/asconfig.json +++ b/test/e2e/contracts/updatable-contract-v2/contract/asconfig.json @@ -1,8 +1,8 @@ { "targets": { "debug": { - "outFile": "build/UpgradeableContract.wasm", - "textFile": "build/UpgradeableContract.wat" + "outFile": "build/UpdatableContractV2.wasm", + "textFile": "build/UpdatableContractV2.wat" } }, "options": { diff --git a/test/e2e/contracts/upgradeable-contract/contract/index.ts b/test/e2e/contracts/updatable-contract-v2/contract/index.ts similarity index 83% rename from test/e2e/contracts/upgradeable-contract/contract/index.ts rename to test/e2e/contracts/updatable-contract-v2/contract/index.ts index 0bb1053..cd63f49 100644 --- a/test/e2e/contracts/upgradeable-contract/contract/index.ts +++ b/test/e2e/contracts/updatable-contract-v2/contract/index.ts @@ -1,5 +1,5 @@ import { Blockchain } from '@btc-vision/btc-runtime/runtime'; -import { UpgradeableContract } from './UpgradeableContract'; +import { UpdatableContractV2 } from './UpdatableContractV2'; import { revertOnError } from '@btc-vision/btc-runtime/runtime/abort/abort'; // DO NOT TOUCH TO THIS. @@ -7,7 +7,7 @@ Blockchain.contract = () => { // ONLY CHANGE THE CONTRACT CLASS NAME. // DO NOT ADD CUSTOM LOGIC HERE. - return new UpgradeableContract(); + return new UpdatableContractV2(); }; // VERY IMPORTANT diff --git a/test/e2e/contracts/upgradeable-contract-v2/contract/tsconfig.json b/test/e2e/contracts/updatable-contract-v2/contract/tsconfig.json similarity index 100% rename from test/e2e/contracts/upgradeable-contract-v2/contract/tsconfig.json rename to test/e2e/contracts/updatable-contract-v2/contract/tsconfig.json diff --git a/test/e2e/contracts/upgradeable-contract/contract/UpgradeableContract.ts b/test/e2e/contracts/updatable-contract/contract/UpdatableContract.ts similarity index 91% rename from test/e2e/contracts/upgradeable-contract/contract/UpgradeableContract.ts rename to test/e2e/contracts/updatable-contract/contract/UpdatableContract.ts index 3c7804b..8a6f1ac 100644 --- a/test/e2e/contracts/upgradeable-contract/contract/UpgradeableContract.ts +++ b/test/e2e/contracts/updatable-contract/contract/UpdatableContract.ts @@ -1,5 +1,4 @@ import { - Address, Blockchain, BytesWriter, Calldata, @@ -7,7 +6,7 @@ import { } from '@btc-vision/btc-runtime/runtime'; @final -export class UpgradeableContract extends OP_NET { +export class UpdatableContract extends OP_NET { public constructor() { super(); } @@ -21,7 +20,7 @@ export class UpgradeableContract extends OP_NET { } @method('address') - public upgrade(calldata: Calldata): BytesWriter { + public update(calldata: Calldata): BytesWriter { const sourceAddress = calldata.readAddress(); Blockchain.updateContractFromExisting(sourceAddress); return new BytesWriter(0); diff --git a/test/e2e/contracts/upgradeable-contract-v2/contract/asconfig.json b/test/e2e/contracts/updatable-contract/contract/asconfig.json similarity index 83% rename from test/e2e/contracts/upgradeable-contract-v2/contract/asconfig.json rename to test/e2e/contracts/updatable-contract/contract/asconfig.json index 17632d1..ca555b6 100644 --- a/test/e2e/contracts/upgradeable-contract-v2/contract/asconfig.json +++ b/test/e2e/contracts/updatable-contract/contract/asconfig.json @@ -1,8 +1,8 @@ { "targets": { "debug": { - "outFile": "build/UpgradeableContractV2.wasm", - "textFile": "build/UpgradeableContractV2.wat" + "outFile": "build/UpdatableContract.wasm", + "textFile": "build/UpdatableContract.wat" } }, "options": { diff --git a/test/e2e/contracts/upgradeable-contract-v2/contract/index.ts b/test/e2e/contracts/updatable-contract/contract/index.ts similarity index 83% rename from test/e2e/contracts/upgradeable-contract-v2/contract/index.ts rename to test/e2e/contracts/updatable-contract/contract/index.ts index d17e068..2db597f 100644 --- a/test/e2e/contracts/upgradeable-contract-v2/contract/index.ts +++ b/test/e2e/contracts/updatable-contract/contract/index.ts @@ -1,5 +1,5 @@ import { Blockchain } from '@btc-vision/btc-runtime/runtime'; -import { UpgradeableContractV2 } from './UpgradeableContractV2'; +import { UpdatableContract } from './UpdatableContract'; import { revertOnError } from '@btc-vision/btc-runtime/runtime/abort/abort'; // DO NOT TOUCH TO THIS. @@ -7,7 +7,7 @@ Blockchain.contract = () => { // ONLY CHANGE THE CONTRACT CLASS NAME. // DO NOT ADD CUSTOM LOGIC HERE. - return new UpgradeableContractV2(); + return new UpdatableContract(); }; // VERY IMPORTANT diff --git a/test/e2e/contracts/upgradeable-contract/contract/tsconfig.json b/test/e2e/contracts/updatable-contract/contract/tsconfig.json similarity index 100% rename from test/e2e/contracts/upgradeable-contract/contract/tsconfig.json rename to test/e2e/contracts/updatable-contract/contract/tsconfig.json diff --git a/test/e2e/contracts/upgradeable-contract/runtime/UpgradeableContractRuntime.ts b/test/e2e/contracts/updatable-contract/runtime/UpdatableContractRuntime.ts similarity index 84% rename from test/e2e/contracts/upgradeable-contract/runtime/UpgradeableContractRuntime.ts rename to test/e2e/contracts/updatable-contract/runtime/UpdatableContractRuntime.ts index f257b9e..8f8b016 100644 --- a/test/e2e/contracts/upgradeable-contract/runtime/UpgradeableContractRuntime.ts +++ b/test/e2e/contracts/updatable-contract/runtime/UpdatableContractRuntime.ts @@ -1,9 +1,9 @@ import { Address, BinaryReader, BinaryWriter } from '@btc-vision/transaction'; import { BytecodeManager, CallResponse, ContractRuntime } from '../../../../../src'; -export class UpgradeableContractRuntime extends ContractRuntime { +export class UpdatableContractRuntime extends ContractRuntime { private readonly getValueSelector: number = this.getSelector('getValue()'); - private readonly upgradeSelector: number = this.getSelector('upgrade(address)'); + private readonly updateSelector: number = this.getSelector('update(address)'); private readonly storeSelector: number = this.getSelector('store(bytes32,bytes32)'); private readonly loadSelector: number = this.getSelector('load(bytes32)'); @@ -26,9 +26,9 @@ export class UpgradeableContractRuntime extends ContractRuntime { return reader.readU32(); } - public async upgrade(sourceAddress: Address): Promise { + public async update(sourceAddress: Address): Promise { const calldata = new BinaryWriter(); - calldata.writeSelector(this.upgradeSelector); + calldata.writeSelector(this.updateSelector); calldata.writeAddress(sourceAddress); const response = await this.execute({ calldata: calldata.getBuffer() }); @@ -57,12 +57,12 @@ export class UpgradeableContractRuntime extends ContractRuntime { } protected handleError(error: Error): Error { - return new Error(`(in upgradeable contract: ${this.address}) OP_NET: ${error.message}`); + return new Error(`(in updatable contract: ${this.address}) OP_NET: ${error.message}`); } protected defineRequiredBytecodes(): void { BytecodeManager.loadBytecode( - './test/e2e/contracts/upgradeable-contract/contract/build/UpgradeableContract.wasm', + './test/e2e/contracts/updatable-contract/contract/build/UpdatableContract.wasm', this.address, ); } diff --git a/test/e2e/imports/UpdateFromAddress.test.ts b/test/e2e/imports/UpdateFromAddress.test.ts index e030e38..64e6d97 100644 --- a/test/e2e/imports/UpdateFromAddress.test.ts +++ b/test/e2e/imports/UpdateFromAddress.test.ts @@ -8,10 +8,10 @@ import { OPNetUnit, StateHandler, } from '../../../src'; -import { UpgradeableContractRuntime } from '../contracts/upgradeable-contract/runtime/UpgradeableContractRuntime'; +import { UpdatableContractRuntime } from '../contracts/updatable-contract/runtime/UpdatableContractRuntime'; const V2_WASM_PATH = - './test/e2e/contracts/upgradeable-contract-v2/contract/build/UpgradeableContractV2.wasm'; + './test/e2e/contracts/updatable-contract-v2/contract/build/UpdatableContractV2.wasm'; class V2SourceContractRuntime extends ContractRuntime { public constructor(deployer: Address, address: Address) { @@ -32,7 +32,7 @@ class V2SourceContractRuntime extends ContractRuntime { } await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { - let contract: UpgradeableContractRuntime; + let contract: UpdatableContractRuntime; let v2Source: V2SourceContractRuntime; const deployerAddress: Address = Blockchain.generateRandomAddress(); @@ -44,7 +44,7 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Blockchain.clearContracts(); await Blockchain.init(); - contract = new UpgradeableContractRuntime(deployerAddress, contractAddress); + contract = new UpdatableContractRuntime(deployerAddress, contractAddress); Blockchain.register(contract); await contract.init(); @@ -63,33 +63,33 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Blockchain.dispose(); }); - // --- Basic upgrade flow --- + // --- Basic update flow --- - await vm.it('should return getValue=1 before upgrade', async () => { + await vm.it('should return getValue=1 before update', async () => { const value = await contract.getValue(); Assert.expect(value).toEqual(1); }); - await vm.it('should not apply upgrade on the same block', async () => { - // Before upgrade, getValue returns 1 + await vm.it('should not apply update on the same block', async () => { + // Before update, getValue returns 1 const valueBefore = await contract.getValue(); Assert.expect(valueBefore).toEqual(1); - // Perform upgrade - await contract.upgrade(v2SourceAddress); + // Perform update + await contract.update(v2SourceAddress); // Same block: getValue should still return 1 (old bytecode) const valueSameBlock = await contract.getValue(); Assert.expect(valueSameBlock).toEqual(1); }); - await vm.it('should apply upgrade on the next block', async () => { - // Before upgrade + await vm.it('should apply update on the next block', async () => { + // Before update const valueBefore = await contract.getValue(); Assert.expect(valueBefore).toEqual(1); - // Perform upgrade - await contract.upgrade(v2SourceAddress); + // Perform update + await contract.update(v2SourceAddress); // Advance to next block Blockchain.mineBlock(); @@ -101,30 +101,30 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { // --- Storage persistence --- - await vm.it('should preserve storage across upgrade', async () => { + await vm.it('should preserve storage across update', async () => { const storageKey = new Uint8Array(32); storageKey[31] = 42; const storageValue = new Uint8Array(32); storageValue[31] = 99; - // Store a value pre-upgrade + // Store a value pre-update await contract.storeValue(storageKey, storageValue); - // Verify it can be loaded before upgrade + // Verify it can be loaded before update const loadedBefore = await contract.loadValue(storageKey); Assert.expect(areBytesEqual(loadedBefore, storageValue)).toEqual(true); - // Upgrade and advance block - await contract.upgrade(v2SourceAddress); + // Update and advance block + await contract.update(v2SourceAddress); Blockchain.mineBlock(); - // Verify storage persists after upgrade with new bytecode + // Verify storage persists after update with new bytecode const loadedAfter = await contract.loadValue(storageKey); Assert.expect(areBytesEqual(loadedAfter, storageValue)).toEqual(true); }); - await vm.it('should preserve multiple storage entries across upgrade', async () => { + await vm.it('should preserve multiple storage entries across update', async () => { const key1 = new Uint8Array(32); key1[31] = 1; const value1 = new Uint8Array(32); @@ -145,8 +145,8 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { await contract.storeValue(key2, value2); await contract.storeValue(key3, value3); - // Upgrade and advance block - await contract.upgrade(v2SourceAddress); + // Update and advance block + await contract.update(v2SourceAddress); Blockchain.mineBlock(); // All values should be preserved @@ -159,9 +159,9 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Assert.expect(areBytesEqual(loaded3, value3)).toEqual(true); }); - await vm.it('should allow writing storage after upgrade', async () => { - // Upgrade and advance block - await contract.upgrade(v2SourceAddress); + await vm.it('should allow writing storage after update', async () => { + // Update and advance block + await contract.update(v2SourceAddress); Blockchain.mineBlock(); // Write new storage with v2 bytecode @@ -176,7 +176,7 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Assert.expect(areBytesEqual(loaded, value)).toEqual(true); }); - await vm.it('should allow reading storage on same block as upgrade (still v1)', async () => { + await vm.it('should allow reading storage on same block as update (still v1)', async () => { const storageKey = new Uint8Array(32); storageKey[31] = 55; const storageValue = new Uint8Array(32); @@ -185,8 +185,8 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { // Store value await contract.storeValue(storageKey, storageValue); - // Upgrade (same block) - await contract.upgrade(v2SourceAddress); + // Update (same block) + await contract.update(v2SourceAddress); // Read on same block — still using v1 bytecode, but storage should be accessible const loaded = await contract.loadValue(storageKey); @@ -199,16 +199,16 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { const nonExistentAddress = Blockchain.generateRandomAddress(); await Assert.expect(async () => { - await contract.upgrade(nonExistentAddress); + await contract.update(nonExistentAddress); }).toThrow(); }); - await vm.it('should remain functional after failed upgrade attempt', async () => { + await vm.it('should remain functional after failed update attempt', async () => { const nonExistentAddress = Blockchain.generateRandomAddress(); - // Attempt upgrade with invalid address (should fail) + // Attempt update with invalid address (should fail) try { - await contract.upgrade(nonExistentAddress); + await contract.update(nonExistentAddress); } catch { // Expected to fail } @@ -218,7 +218,7 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Assert.expect(value).toEqual(1); }); - await vm.it('should preserve storage after failed upgrade attempt', async () => { + await vm.it('should preserve storage after failed update attempt', async () => { const storageKey = new Uint8Array(32); storageKey[31] = 11; const storageValue = new Uint8Array(32); @@ -228,7 +228,7 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { const nonExistentAddress = Blockchain.generateRandomAddress(); try { - await contract.upgrade(nonExistentAddress); + await contract.update(nonExistentAddress); } catch { // Expected to fail } @@ -240,10 +240,10 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { // --- Address preservation --- - await vm.it('should preserve contract address after upgrade', async () => { + await vm.it('should preserve contract address after update', async () => { const addressBefore = contract.address; - await contract.upgrade(v2SourceAddress); + await contract.update(v2SourceAddress); Blockchain.mineBlock(); Assert.expect(contract.address.equals(addressBefore)).toEqual(true); @@ -251,8 +251,8 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { // --- Block boundary behavior --- - await vm.it('should not apply upgrade until block advances', async () => { - await contract.upgrade(v2SourceAddress); + await vm.it('should not apply update until block advances', async () => { + await contract.update(v2SourceAddress); // Same block: multiple calls should all return v1 value const value1 = await contract.getValue(); @@ -268,8 +268,8 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Assert.expect(value3).toEqual(2); }); - await vm.it('should apply upgrade exactly at next block boundary', async () => { - await contract.upgrade(v2SourceAddress); + await vm.it('should apply update exactly at next block boundary', async () => { + await contract.update(v2SourceAddress); // Same block Assert.expect(await contract.getValue()).toEqual(1); @@ -277,7 +277,7 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { // Mine one block Blockchain.mineBlock(); - // Exactly one block later: upgrade should be applied + // Exactly one block later: update should be applied Assert.expect(await contract.getValue()).toEqual(2); // Mine another block: should still be v2 @@ -288,10 +288,10 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { // --- Gas tracking --- - await vm.it('should consume gas during upgrade', async () => { - const response = await contract.upgrade(v2SourceAddress); + await vm.it('should consume gas during update', async () => { + const response = await contract.update(v2SourceAddress); - // Upgrade execution must consume gas + // Update execution must consume gas Assert.expect(response.usedGas > 0n).toEqual(true); }); @@ -307,21 +307,21 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { }); const simpleGas = simpleResponse.usedGas; - // Measure gas for upgrade (includes onUpdate + bytecode overhead) - const upgradeResponse = await contract.upgrade(v2SourceAddress); - const upgradeGas = upgradeResponse.usedGas; + // Measure gas for update (includes onUpdate + bytecode overhead) + const updateResponse = await contract.update(v2SourceAddress); + const updateGas = updateResponse.usedGas; - // Upgrade should cost more than a trivial getter - Assert.expect(upgradeGas > simpleGas).toEqual(true); + // Update should cost more than a trivial getter + Assert.expect(updateGas > simpleGas).toEqual(true); }); - await vm.it('should not charge upgrade gas on failed upgrade', async () => { + await vm.it('should not charge update gas on failed update', async () => { const nonExistentAddress = Blockchain.generateRandomAddress(); - // Failed upgrade should still return a response (with error) + // Failed update should still return a response (with error) // but the overall tx reverts so gas is handled by the VM await Assert.expect(async () => { - await contract.upgrade(nonExistentAddress); + await contract.update(nonExistentAddress); }).toThrow(); // Contract should still be functional (no gas state corruption) @@ -329,15 +329,15 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Assert.expect(value).toEqual(1); }); - // --- One upgrade per block enforcement --- + // --- One update per block enforcement --- - await vm.it('should reject second upgrade in the same block', async () => { - // First upgrade should succeed - await contract.upgrade(v2SourceAddress); + await vm.it('should reject second update in the same block', async () => { + // First update should succeed + await contract.update(v2SourceAddress); - // Second upgrade in the same block should revert + // Second update in the same block should revert await Assert.expect(async () => { - await contract.upgrade(v2SourceAddress); + await contract.update(v2SourceAddress); }).toThrow(); // Contract should still work @@ -346,14 +346,14 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { }); await vm.it('should clear pending state after block advances', async () => { - // Upgrade and advance - await contract.upgrade(v2SourceAddress); + // Update and advance + await contract.update(v2SourceAddress); Blockchain.mineBlock(); - // Upgrade applied — getValue returns 2 + // Update applied — getValue returns 2 Assert.expect(await contract.getValue()).toEqual(2); - // Mine another block — no pending upgrade, should still work + // Mine another block — no pending update, should still work Blockchain.mineBlock(); Assert.expect(await contract.getValue()).toEqual(2); @@ -368,21 +368,21 @@ await opnet('UpdateFromAddress tests', async (vm: OPNetUnit) => { Assert.expect(areBytesEqual(loaded, value)).toEqual(true); }); - // --- Upgrade + subsequent operations --- + // --- Update + subsequent operations --- - await vm.it('should allow operations between upgrade and block advance', async () => { + await vm.it('should allow operations between update and block advance', async () => { const storageKey = new Uint8Array(32); storageKey[31] = 100; const storageValue = new Uint8Array(32); storageValue[31] = 200; - // Upgrade - await contract.upgrade(v2SourceAddress); + // Update + await contract.update(v2SourceAddress); - // Store data between upgrade and block advance (still v1) + // Store data between update and block advance (still v1) await contract.storeValue(storageKey, storageValue); - // Mine block to apply upgrade + // Mine block to apply update Blockchain.mineBlock(); // Data written before block advance should persist with v2 @@ -432,8 +432,8 @@ await opnet('BytecodeManager targeted removal', async (vm: OPNetUnit) => { }); }); -await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { - let contract: UpgradeableContractRuntime; +await opnet('Update lifecycle edge cases', async (vm: OPNetUnit) => { + let contract: UpdatableContractRuntime; let v2Source: V2SourceContractRuntime; const deployerAddress: Address = Blockchain.generateRandomAddress(); @@ -445,7 +445,7 @@ await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { Blockchain.clearContracts(); await Blockchain.init(); - contract = new UpgradeableContractRuntime(deployerAddress, contractAddress); + contract = new UpdatableContractRuntime(deployerAddress, contractAddress); Blockchain.register(contract); await contract.init(); @@ -463,8 +463,8 @@ await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { Blockchain.dispose(); }); - await vm.it('should allow multiple calls after successful upgrade + mine', async () => { - await contract.upgrade(v2SourceAddress); + await vm.it('should allow multiple calls after successful update + mine', async () => { + await contract.update(v2SourceAddress); Blockchain.mineBlock(); // Multiple sequential calls on new bytecode @@ -473,8 +473,8 @@ await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { Assert.expect(await contract.getValue()).toEqual(2); }); - await vm.it('should allow storage operations on new bytecode after upgrade', async () => { - await contract.upgrade(v2SourceAddress); + await vm.it('should allow storage operations on new bytecode after update', async () => { + await contract.update(v2SourceAddress); Blockchain.mineBlock(); // Write with new bytecode @@ -488,8 +488,8 @@ await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { Assert.expect(areBytesEqual(loaded, value)).toEqual(true); }); - await vm.it('should handle upgrade followed by multiple block advances', async () => { - await contract.upgrade(v2SourceAddress); + await vm.it('should handle update followed by multiple block advances', async () => { + await contract.update(v2SourceAddress); // Mine 3 blocks Blockchain.mineBlock(); @@ -500,19 +500,19 @@ await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { Assert.expect(await contract.getValue()).toEqual(2); }); - await vm.it('should not corrupt state after failed upgrade + successful retry', async () => { + await vm.it('should not corrupt state after failed update + successful retry', async () => { const fakeAddr = Blockchain.generateRandomAddress(); - // Store value before any upgrades + // Store value before any updates const key = new Uint8Array(32); key[31] = 0x01; const value = new Uint8Array(32); value[31] = 0xff; await contract.storeValue(key, value); - // Failed upgrade + // Failed update try { - await contract.upgrade(fakeAddr); + await contract.update(fakeAddr); } catch { // expected } @@ -524,8 +524,8 @@ await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { const loaded = await contract.loadValue(key); Assert.expect(areBytesEqual(loaded, value)).toEqual(true); - // Now do a real upgrade - await contract.upgrade(v2SourceAddress); + // Now do a real update + await contract.update(v2SourceAddress); Blockchain.mineBlock(); // V2 active and storage preserved @@ -536,7 +536,7 @@ await opnet('Upgrade lifecycle edge cases', async (vm: OPNetUnit) => { }); await opnet('Phase 2 gas accounting', async (vm: OPNetUnit) => { - let contract: UpgradeableContractRuntime; + let contract: UpdatableContractRuntime; let v2Source: V2SourceContractRuntime; const deployerAddress: Address = Blockchain.generateRandomAddress(); @@ -548,7 +548,7 @@ await opnet('Phase 2 gas accounting', async (vm: OPNetUnit) => { Blockchain.clearContracts(); await Blockchain.init(); - contract = new UpgradeableContractRuntime(deployerAddress, contractAddress); + contract = new UpdatableContractRuntime(deployerAddress, contractAddress); Blockchain.register(contract); await contract.init(); @@ -567,7 +567,7 @@ await opnet('Phase 2 gas accounting', async (vm: OPNetUnit) => { }); await vm.it('should charge Phase 2 onUpdate gas to the first caller after mine', async () => { - // Baseline: gas for getValue without pending upgrade + // Baseline: gas for getValue without pending update const abiCoder = new ABICoder(); const getValueCalldata = new BinaryWriter(); getValueCalldata.writeSelector( @@ -579,29 +579,29 @@ await opnet('Phase 2 gas accounting', async (vm: OPNetUnit) => { }); const baselineGas = baselineResponse.usedGas; - // Queue upgrade, mine block - await contract.upgrade(v2SourceAddress); + // Queue update, mine block + await contract.update(v2SourceAddress); Blockchain.mineBlock(); - // First call after mine triggers Phase 2 (applyPendingBytecodeUpgrade) + // First call after mine triggers Phase 2 (applyPendingBytecodeUpdate) const getValueCalldata2 = new BinaryWriter(); getValueCalldata2.writeSelector( Number(`0x${abiCoder.encodeSelector('getValue()')}`), ); - const postUpgradeResponse = await contract.execute({ + const postUpdateResponse = await contract.execute({ calldata: getValueCalldata2.getBuffer(), }); - const postUpgradeGas = postUpgradeResponse.usedGas; + const postUpdateGas = postUpdateResponse.usedGas; // Phase 2 onUpdate gas MUST be included — first call costs more than baseline - Assert.expect(postUpgradeGas > baselineGas).toEqual(true); + Assert.expect(postUpdateGas > baselineGas).toEqual(true); }); await vm.it('should NOT charge Phase 2 gas on second call (already applied)', async () => { const abiCoder = new ABICoder(); - await contract.upgrade(v2SourceAddress); + await contract.update(v2SourceAddress); Blockchain.mineBlock(); // First call: pays Phase 2 gas @@ -610,7 +610,7 @@ await opnet('Phase 2 gas accounting', async (vm: OPNetUnit) => { const firstCallResponse = await contract.execute({ calldata: calldata1.getBuffer() }); const firstCallGas = firstCallResponse.usedGas; - // Second call: no pending upgrade, normal gas + // Second call: no pending update, normal gas const calldata2 = new BinaryWriter(); calldata2.writeSelector(Number(`0x${abiCoder.encodeSelector('getValue()')}`)); const secondCallResponse = await contract.execute({ calldata: calldata2.getBuffer() }); @@ -621,8 +621,8 @@ await opnet('Phase 2 gas accounting', async (vm: OPNetUnit) => { }); }); -await opnet('Phase 2 upgrade guard enforcement', async (vm: OPNetUnit) => { - let contract: UpgradeableContractRuntime; +await opnet('Phase 2 update guard enforcement', async (vm: OPNetUnit) => { + let contract: UpdatableContractRuntime; let v2Source: V2SourceContractRuntime; const deployerAddress: Address = Blockchain.generateRandomAddress(); @@ -634,7 +634,7 @@ await opnet('Phase 2 upgrade guard enforcement', async (vm: OPNetUnit) => { Blockchain.clearContracts(); await Blockchain.init(); - contract = new UpgradeableContractRuntime(deployerAddress, contractAddress); + contract = new UpdatableContractRuntime(deployerAddress, contractAddress); Blockchain.register(contract); await contract.init(); @@ -652,8 +652,8 @@ await opnet('Phase 2 upgrade guard enforcement', async (vm: OPNetUnit) => { Blockchain.dispose(); }); - await vm.it('should clear upgrade guard after Phase 2 completes', async () => { - await contract.upgrade(v2SourceAddress); + await vm.it('should clear update guard after Phase 2 completes', async () => { + await contract.update(v2SourceAddress); Blockchain.mineBlock(); // First call triggers Phase 2. Guard is set during onUpdate, cleared after. @@ -673,18 +673,18 @@ await opnet('Phase 2 upgrade guard enforcement', async (vm: OPNetUnit) => { Assert.expect(areBytesEqual(loaded, value)).toEqual(true); }); - await vm.it('should allow upgrade on a later block after Phase 2 completed', async () => { - // First upgrade cycle - await contract.upgrade(v2SourceAddress); + await vm.it('should allow update on a later block after Phase 2 completed', async () => { + // First update cycle + await contract.update(v2SourceAddress); Blockchain.mineBlock(); Assert.expect(await contract.getValue()).toEqual(2); - // Re-register V1 source to allow upgrading back (simulate V3) + // Re-register V1 source to allow updating back (simulate V3) // Use V2 source again — just proving the mechanism allows it Blockchain.mineBlock(); // The guard from Phase 2 must be fully cleared - // A new upgrade request should succeed on a new block + // A new update request should succeed on a new block const key = new Uint8Array(32); key[31] = 0x01; const value = new Uint8Array(32); @@ -731,7 +731,7 @@ class MaliciousV2SourceRuntime extends ContractRuntime { // emits a "PhantomEvent", then reverts. These side effects MUST NOT leak. await opnet('Failed Phase 2 state isolation', async (vm: OPNetUnit) => { - let contract: UpgradeableContractRuntime; + let contract: UpdatableContractRuntime; let maliciousV2Source: MaliciousV2SourceRuntime; const deployerAddress: Address = Blockchain.generateRandomAddress(); @@ -743,7 +743,7 @@ await opnet('Failed Phase 2 state isolation', async (vm: OPNetUnit) => { Blockchain.clearContracts(); await Blockchain.init(); - contract = new UpgradeableContractRuntime(deployerAddress, contractAddress); + contract = new UpdatableContractRuntime(deployerAddress, contractAddress); Blockchain.register(contract); await contract.init(); @@ -762,8 +762,8 @@ await opnet('Failed Phase 2 state isolation', async (vm: OPNetUnit) => { }); await vm.it('should NOT leak storage writes from failed Phase 2 onUpdate', async () => { - // Queue upgrade to malicious V2 (Phase 1 succeeds on current bytecode) - await contract.upgrade(maliciousV2Address); + // Queue update to malicious V2 (Phase 1 succeeds on current bytecode) + await contract.update(maliciousV2Address); // Advance block to trigger Phase 2 Blockchain.mineBlock(); @@ -794,7 +794,7 @@ await opnet('Failed Phase 2 state isolation', async (vm: OPNetUnit) => { }); await vm.it('should NOT leak events from failed Phase 2 onUpdate', async () => { - await contract.upgrade(maliciousV2Address); + await contract.update(maliciousV2Address); Blockchain.mineBlock(); const abiCoder = new ABICoder(); @@ -804,16 +804,16 @@ await opnet('Failed Phase 2 state isolation', async (vm: OPNetUnit) => { // No "PhantomEvent" should appear in the transaction events const phantomEvents = response.events.filter( - (e) => e.eventType === 'PhantomEvent', + (e) => e.type === 'PhantomEvent', ); Assert.expect(phantomEvents.length).toEqual(0); }); await vm.it('should charge gas for failed Phase 2 onUpdate', async () => { - await contract.upgrade(maliciousV2Address); + await contract.update(maliciousV2Address); Blockchain.mineBlock(); - // Baseline: normal getValue gas without any pending upgrade + // Baseline: normal getValue gas without any pending update const abiCoder = new ABICoder(); // First call: triggers failed Phase 2 + getValue @@ -822,7 +822,7 @@ await opnet('Failed Phase 2 state isolation', async (vm: OPNetUnit) => { const firstResponse = await contract.execute({ calldata: calldata1.getBuffer() }); const gasWithFailedPhase2 = firstResponse.usedGas; - // Second call: no pending upgrade, clean getValue + // Second call: no pending update, clean getValue const calldata2 = new BinaryWriter(); calldata2.writeSelector(Number(`0x${abiCoder.encodeSelector('getValue()')}`)); const secondResponse = await contract.execute({ calldata: calldata2.getBuffer() }); @@ -833,7 +833,7 @@ await opnet('Failed Phase 2 state isolation', async (vm: OPNetUnit) => { }); await vm.it('should revert bytecode after failed Phase 2 (still v1)', async () => { - await contract.upgrade(maliciousV2Address); + await contract.update(maliciousV2Address); Blockchain.mineBlock(); // getValue should return 1 (v1) — Phase 2 failed, bytecode reverted