From 39bfbb4b37ef7f0148db6b9e5517c80876756f29 Mon Sep 17 00:00:00 2001 From: John Kane Date: Wed, 20 Sep 2023 17:32:27 +0100 Subject: [PATCH] fix: allow undefined froms to pass reconciliation If a `from` is undefined on the first run, allow it to reconcile on the second run using the account that was selected during initialization of the future on the first run. This is only valid if the first run from account (the default sender on the first run) is still in the list of accounts. Fixes #413. --- .../reconciliation/helpers/reconcile-from.ts | 8 +++- .../futures/reconcileNamedContractCall.ts | 34 +++++++++++++++++ .../reconcileNamedContractDeployment.ts | 28 ++++++++++++++ .../futures/reconcileNamedStaticCall.ts | 37 +++++++++++++++++++ .../futures/reconcileSendData.ts | 25 +++++++++++++ 5 files changed, 130 insertions(+), 2 deletions(-) diff --git a/packages/core/src/internal/reconciliation/helpers/reconcile-from.ts b/packages/core/src/internal/reconciliation/helpers/reconcile-from.ts index 287be1562..f9fe4e33a 100644 --- a/packages/core/src/internal/reconciliation/helpers/reconcile-from.ts +++ b/packages/core/src/internal/reconciliation/helpers/reconcile-from.ts @@ -1,11 +1,11 @@ import { + ContractCallFuture, ContractDeploymentFuture, LibraryDeploymentFuture, - ContractCallFuture, NamedArtifactContractDeploymentFuture, NamedArtifactLibraryDeploymentFuture, - StaticCallFuture, SendDataFuture, + StaticCallFuture, } from "../../../types/module"; import { resolveFutureFrom } from "../../execution/future-processor/helpers/future-resolvers"; import { @@ -37,6 +37,10 @@ export function reconcileFrom( | StaticCallExecutionState, context: ReconciliationContext ): ReconciliationFutureResultFailure | undefined { + if (future.from === undefined && context.accounts.includes(exState.from)) { + return undefined; + } + const resolvedFrom = resolveFutureFrom( future.from, context.accounts, diff --git a/packages/core/test/reconciliation/futures/reconcileNamedContractCall.ts b/packages/core/test/reconciliation/futures/reconcileNamedContractCall.ts index 37aa17c44..88cc138e8 100644 --- a/packages/core/test/reconciliation/futures/reconcileNamedContractCall.ts +++ b/packages/core/test/reconciliation/futures/reconcileNamedContractCall.ts @@ -94,6 +94,40 @@ describe("Reconciliation - named contract call", () => { ); }); + it("should reconcile when the from is undefined but the exState's from is in the accounts list", async () => { + const moduleDefinition = buildModule("Module", (m) => { + const contract1 = m.contract("Contract1"); + + m.call(contract1, "function1", [1, "a", contract1], { from: undefined }); + + return { contract1 }; + }); + + await assertSuccessReconciliation( + moduleDefinition, + createDeploymentState( + { + ...exampleDeploymentState, + id: "Module#Contract1", + status: ExecutionStatus.SUCCESS, + result: { + type: ExecutionResultType.SUCCESS, + address: differentAddress, + }, + }, + { + ...exampleContractCallState, + id: "Module#Contract1.function1", + futureType: FutureType.CONTRACT_CALL, + status: ExecutionStatus.SUCCESS, + functionName: "function1", + args: [1, "a", differentAddress], + from: exampleAccounts[2], + } + ) + ); + }); + it("should find changes to contract unreconciliable", async () => { const moduleDefinition = buildModule("Module", (m) => { const contract1 = m.contract("Contract1"); diff --git a/packages/core/test/reconciliation/futures/reconcileNamedContractDeployment.ts b/packages/core/test/reconciliation/futures/reconcileNamedContractDeployment.ts index 7271abf4e..3ba19a6b0 100644 --- a/packages/core/test/reconciliation/futures/reconcileNamedContractDeployment.ts +++ b/packages/core/test/reconciliation/futures/reconcileNamedContractDeployment.ts @@ -96,6 +96,34 @@ describe("Reconciliation - named contract", () => { ); }); + /** + * This test here is in a first run, the from is undefined and the defaultSender is used. + * On the second run the from is undefined but a different defaultSender is now in play. + * We say this should reconcile but the account from the first run should be used, as long + * as it is in the accounts list + */ + it("should reconcile where the future is undefined but the exState's from is in the accounts list", async () => { + const moduleDefinition = buildModule("Module", (m) => { + const contract1 = m.contract("Contract", [], { + id: "Example", + from: undefined, + }); + + return { contract1 }; + }); + + await assertSuccessReconciliation( + moduleDefinition, + createDeploymentState({ + ...exampleDeploymentState, + id: "Module#Example", + status: ExecutionStatus.STARTED, + contractName: "Contract", + from: exampleAccounts[3], + }) + ); + }); + it("should find changes to contract name unreconciliable", async () => { const moduleDefinition = buildModule("Module", (m) => { const contract1 = m.contract("ContractChanged", [], { diff --git a/packages/core/test/reconciliation/futures/reconcileNamedStaticCall.ts b/packages/core/test/reconciliation/futures/reconcileNamedStaticCall.ts index 691ec4c33..e8892a548 100644 --- a/packages/core/test/reconciliation/futures/reconcileNamedStaticCall.ts +++ b/packages/core/test/reconciliation/futures/reconcileNamedStaticCall.ts @@ -95,6 +95,43 @@ describe("Reconciliation - named static call", () => { ); }); + it("should reconcile when the from is undefined but the exState's from is in the accounts list", async () => { + const moduleDefinition = buildModule("Module", (m) => { + const contract1 = m.contract("Contract1"); + + m.staticCall(contract1, "function1", [1, "a"], 0, { + from: undefined, + }); + + return { contract1 }; + }); + + await assertSuccessReconciliation( + moduleDefinition, + createDeploymentState( + { + ...exampleDeploymentState, + id: "Module#Contract1", + status: ExecutionStatus.SUCCESS, + result: { + type: ExecutionResultType.SUCCESS, + address: exampleAddress, + }, + }, + { + ...exampleStaticCallState, + id: "Module#Contract1.function1", + futureType: FutureType.STATIC_CALL, + status: ExecutionStatus.SUCCESS, + contractAddress: exampleAddress, + functionName: "function1", + args: [1, "a"], + from: exampleAccounts[4], + } + ) + ); + }); + it("should find changes to contract unreconciliable", async () => { const moduleDefinition = buildModule("Module", (m) => { const contract1 = m.contract("Contract1"); diff --git a/packages/core/test/reconciliation/futures/reconcileSendData.ts b/packages/core/test/reconciliation/futures/reconcileSendData.ts index 06dca8e5c..6303f3658 100644 --- a/packages/core/test/reconciliation/futures/reconcileSendData.ts +++ b/packages/core/test/reconciliation/futures/reconcileSendData.ts @@ -56,6 +56,31 @@ describe("Reconciliation - send data", () => { ); }); + /** + * This test here is in a first run, the from is undefined and the defaultSender is used. + * On the second run the from is undefined but a different defaultSender is now in play. + * We say this should reconcile but the account from the first run should be used, as long + * as it is in the accounts list. + */ + it("should reconcile where the future is undefined but the exState's from is in the accounts list", async () => { + const moduleDefinition = buildModule("Module", (m) => { + m.send("test_send", exampleAddress, 0n, "example_data", { + from: undefined, + }); + + return {}; + }); + + await assertSuccessReconciliation( + moduleDefinition, + createDeploymentState({ + ...exampleSendState, + id: "Module#test_send", + status: ExecutionStatus.STARTED, + }) + ); + }); + it("should reconcile between undefined and 0x for data", async () => { const moduleDefinition = buildModule("Module", (m) => { m.send("test_send", exampleAddress, 0n, undefined);