Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LF: fix contract ID freshness check #9370

Merged
merged 11 commits into from Apr 13, 2021
Merged

Conversation

remyhaemmerle-da
Copy link
Collaborator

@remyhaemmerle-da remyhaemmerle-da commented Apr 9, 2021

Local contract ids are collected in exercise children during
preprocessing transaction for replay.

CHANGELOG_BEGIN

  • [Engine] Fix contract ID fresness check when validating transaction

CHANGELOG_END

Pull Request Checklist

  • Read and understand the contribution guidelines
  • Include appropriate tests
  • Set a descriptive title and thorough description
  • Add a reference to the issue this PR will solve, if appropriate
  • Include changelog additions in one or more commit message bodies between the CHANGELOG_BEGIN and CHANGELOG_END tags
  • Normal production system change, include purpose of change in description

NOTE: CI is not automatically run on non-members pull-requests for security
reasons. The reviewer will have to comment with /AzurePipelines run to
trigger the build.

Local contract ids are collected in exercise children during
preprocessing transaction for replay.

CHANGELOG_BEGIN
- [Engine] Fix contract ID fresness check when validating transaction
CHANGELOG_END
@remyhaemmerle-da remyhaemmerle-da marked this pull request as ready for review April 9, 2021 13:08
@remyhaemmerle-da remyhaemmerle-da added the component/daml-engine DAML-LF Engine & Interpreter label Apr 9, 2021
@remyhaemmerle-da remyhaemmerle-da added this to the Maintenance milestone Apr 9, 2021
Copy link
Contributor

@cocreature cocreature left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for fixing this so quickly!

Comment on lines 103 to 104
if (newLocals.exists(globalCids))
fail("Conflicting discriminators between a global and local contract ID.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get a testcase for this? This seems like a related but not the same bug. I think you can trigger it on a transaction like the following:

  1. First do an exercise on a global cid.
  2. Then do a create which produces the same discriminator as the global cid.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed that this just got shuffled around but I still don’t see an existing test for it so it would be nice to add it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will double check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appear the test is not 100% necessary, as the dynamic check performed by the reinterpretation will catch those problematic cases.
I still prefer to let the test here. I add a comment and a test.

}
}
val globalCids = globalCids0 | newGlobals
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that this works? I'm a bit uneasy that we can't distinguish the following two scenarios:

Scenario 1:

  1. There is a contract with cid cid0 on the ledger.
  2. We submit a two-command transaction: Command 1's transaction tree contains a create node that creates a contract with local cid cid0. Command 2 is a plain exercise on the contract cid0 that exists on the ledger. However, replay now interprets this command as acting on the newly created contract.

Scenario 2:

  1. There is no contract with cid cid0 on the ledger.
  2. We submit a two-command transaction: Command 1's transaction tree contains a create node that creates a contract with local cid cid0 and key k. Command 2 is a by-key exercise on the key k.

One could probably argue that scenario 1 should have failed during interpretation in the submission service, but I'm worried that this subtlety gets overlooked at some point in the future and we get the next bug here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good point, we definitely violate the spec here, quoting:

* The *local* contract IDs are the IDs of the contracts created by the
  transaction.

* The *global* contract IDs are the contract IDs that:
   
   * appear in the commands that produced the transaction. This
     includes the IDs of the exercised contract, together with all the
     IDs referenced in the arguments of the create and exercise
     commands;
   * that are fetched or looked up by key unless they are local;
   * are referenced in the input contracts.

so cid0 should definitely be global due to the exercise and we should get a conflict.

Not really sure how to fix that. It seems like the only way out is to preserve byKey info on exercise which we should do either way but that doesn’t help for old transaction versions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After another offline discussion with @remyhaemmerle-da: There are probably two ways to approach this;

  1. Properly distinguish between normal exercise and exerciseByKey nodes using the byKey flag. For backwards compatibility, we could treat all exercise nodes without a byKey field as normal exercises. This preserves the current behaviour, but still needs special treatment for CreateAndExercise commands.
  2. In general allow exercises in later commands to use contract IDs created in earlier commands of the same transaction (both as input contract and in their choice arguments). This is the direction of this PR.

In scenario 1, the created contract clashes with an existing contract. So the Daml ledger should reject the submission with Contract cid0 already exists. The interesting part here is that the existing cid0 might have been archived long ago (and just sits in the submitting participant's store of divulged contracts, so pruning may have removed the existence of cid0 from the ledger's memory, but then the skew bounds on the ledger time (a contract ID commits to the ledger time of the creating transaction) should be tight enough that cid0 cannot be created in this transaction.

So, we could update the spec to say that if a later exercise command refers to a contract created by an earlier command, then that's not considered a global contract ID for the transaction. What should we do about an exercise command referring to a contract ID that's created in its consequences or by a later command? I'd argue that this is an internally inconsistent transaction and should be rejected, and AFAICS this PR implements this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are two mismatches which bite us here both of which we should fix but they are slightly orthogonal:

  1. The mismatch between transaction nodes and LF primitives. This affects by-key flags but not createAndExercise (there is no primitive for that in LF)
  2. The mismatch between transaction nodes and ledger API commands. This affects by-key flags but only on root nodes and createAndExercise.

We should absolutely fix 1 and add by-key flags but as you pointed out that does not make the issue go away for createAndExercise. Just doing that leads to what I’d argue are slightly unintuitive semantics: If you modify your scenario 1 such that the exercise in the second command is not the root node, this is already perfectly fine. Your approach 2 (implemented in this PR but probably should also get a spec update) removes that annoying special treatment of root nodes from the cid freshness check afaict. That seems like a more robust solution to address mismatch 2 (in this instance). I’m not really qualified to judge if relaxing the CID check here causes any issues but it sounds like you’re saying it’s fine. Assuming that is the case, my preference would be the following two steps:

  1. Implement approach 2, i.e., this PR but also update the spec to match this (can be in a separate PR). This is all we need to fix the CID freshness check.
  2. Add byKey flags to transaction nodes after that (soonish). This address mismatch 1 which causes other issues.

wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a problem with relaxing the CID check here, as this should be caught on a higher level (freshness check for created contracts). I recommend to document the need for this freshness check; otherwise we'll forget and eventually run into a problem with some of the Daml ledgers.

The byKey flag is orthogonal in this approach. I would like to see the flag added officially at some point; it's not just at root nodes though, because child nodes can be come root nodes via projection.

Copy link
Contributor

@andreaslochbihler-da andreaslochbihler-da left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a concern in one comment. I think this works for the moment, but there's the danger that two commands with different semantics might yield the same preprocessing result. It's more a case of being cautious.

* * `n` is a create node and `cid` appear in the payload of the create contract (`n.arg`)
* * `n` is an exercise node and `cid` appear in the exercise argument (`n.choosenValue`)
* * `n` is an exercise node and `cid` is the ID of the exercise contract (`n.taretCoid`)
* A contract ID is *global* w.r.t a transaction `tx` if it is global w.r.t one of the root `tx`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps worth pointing out in the comment that this is only parts of the definition of global cids. In particular, a contract id is also global if it is referenced in the arguments of a global fetched or lookup’d contract. We detect those cases during interpretation so no need to add it here, just asking to clarify the comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

* A contract ID `cid` is considered *global* in a root node `n`,
* if:
* - `cid` is not considered local w.r.t. `n`, and
* - if `cid` is an input of a `n`, i.e. :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* - if `cid` is an input of a `n`, i.e. :
* - if `cid` is referenced in `n`, i.e. :

Input contract is typically used for the contract ID that an exercise acts on, not the ids that appear in the choice argument.

* and collect the global contract IDs.
* A contract ID `cid` is considered *local* w.r.t. a node `n`, if
* either:
* - it is local in a previous node w.r.t. traversal order, or
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to make clear that the traversal order visits all nodes of a transaction (rather than just the root nodes, which is what the rest of this comment is about)?

Copy link
Contributor

@andreaslochbihler-da andreaslochbihler-da left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few stylistic comments.

@remyhaemmerle-da remyhaemmerle-da changed the title LF: fix contract id freshness check LF: fix contract ID freshness check Apr 13, 2021
remyhaemmerle-da and others added 3 commits April 13, 2021 12:47
Co-authored-by: Andreas Lochbihler <36634420+andreaslochbihler-da@users.noreply.github.com>
@remyhaemmerle-da remyhaemmerle-da merged commit 2dc09ba into main Apr 13, 2021
@remyhaemmerle-da remyhaemmerle-da deleted the remy-lf-cid-freshness-fix-2 branch April 13, 2021 16:45
azure-pipelines bot pushed a commit that referenced this pull request Apr 14, 2021
This PR has been created by a script, which is not very smart
and does not have all the context. Please do double-check that
the version prefix is correct before merging.

@remyhaemmerle-da is in charge of this release.

Commit log:
```
2dc09ba LF: fix contract ID freshness check (#9370)
1627b70 Pattern matching for RoundingMode (#9381)
b41e1ed fix daml-lf/governance.rst (#9399)
8f885f4 MutableCacheBackedContractStore implementation (#9378)
7890381 Publish EE JSON API to artifactory (#9392)
6ab9655 Fix scala version in quickstart-scala (#9401)
87d3f89 offboarding @hurryabit (#9398)
29fcebe Fix recording of  cache metrics [KVL-888] (#9382)
67b0e2b Drop hurryabit from release rotation 😭 (#9393)
24c64ce Clean up participant-integration-api command line help page (#9385)
2f4b32f update NOTICES file (#9391)
cda2940 KVL-861 Add support for dumping expected and actual updates in the integrity checker (#9379)
7fc86b7 Fix flaky StateCacheSpec test (#9389)
301dcd9 DPP-316: Enable the use of the append only index database (#9368)
9ac74e6 Oracle json-api: websockets and testing (#9278)
104ad06 Streaming contract state events (on the append-only schema) [DPP-304] (#9365)
da6a0d6 Extend prometheus metrics with min/mean/max values (#9380)
10edc66 Implement Prometheus metrics back-end (#9373)
89b5dbb LedgerDao and ContractsReader interface updates (#9349)
8480032 daml package: bump timeout for tests (#9377)
bca24a9 Make LogEntryId computation strategy injectable to submission validators (#9302)
78dc238 StateCache implementation for mutable contract state (#9299)
91b65e8 Patch hoogle binary to include bugfix (#9366)
dc4b9e5 Publish trigger service EE fat JARs (#9363)
11e5dd3 link to postgres docs (#9353)
f84b6ab daml build: add a --access-token-file for remote dependencies (#9358)
dfe26b9 fix hoogle (#9364)
c220e05 infra/hoogle: use nix (#9362)
5a983e3 Deduplicate LfValueTranslation cache (#9354)
e84c954 Expose Oracle support in the EE trigger service (#9342)
0303017 Delete "testing with scenarios" section (#9360)
587bff2 Fix comment formatting in state (#9359)
4c231dc Update canton tests to pre 0.23.0 release (#9356)
bc4e00b Run Canton tests with JDK11 (#9355)
948d4dd infra: hoogle blue/green tf (#9351)
69ecf57 Removing Git as a prerequisit from the GSG (#9202)
2745bc0 macos: move cache setup to step 2 (#9350)
38c417e bump hoogle ubuntu (#9344)
a2ccf1b LF: release LF 1.13 archive snapshot (#9348)
867e625 Add exception handling to Daml Script (#9324)
c97db24 fix macOS cache cleaning (#9343)
bac3521 Add sqrt to DA.Math (#9346)
06701f7 Expose rounding modes as constructors + add BigNumeric docs. (#9336)
b90a9c3 Remove exception message from AnyException (#9328)
35759fc LF: Freeze archive proto for LF 1.13 (#9345)
0251e93 Improve TX normalization. (#9341)
2b1f882 daml-ledger: new list-packages command (#9325)
568a852 LF: release preview of LF 1.13 (#9329)
8b8f736 update compat versions for 1.12.0-snapshot.20210406.6646.0.631db446 (#9332)
960134d Fix typo in profiler docs, file name starts with profile (not profiler) (#9338)
0eac00f Avoid deprecated forHostWithLedgerIdDiscovery in quickstart-java (#9337)
5b11571 Introduce parallel indexer (#9304)
e84a94e Update issue number for BigNumeric Divisible instance. (#9334)
8f7ef05 Avoid mention of scenarios in Sandbox docs (#9335)
93de56d Optimize race condition integration tests [DPP-330] (#9320)
74956e2 LF: BigNumeric spec. (#8899)
4d0c6db daml ledger: better error messages for missing host/port args (#9322)
247a1a3 snapshot to test non-repudiation publish (#9326)
7b3b669 LF: add test for bigNumeric operations (#9310)
fd63cf0 use {tag, value} format for SQLizing advanced JSON queries (#9321)
```
Changelog:
```
- [Engine] Fix contract ID freshness check when validating transaction
- [Integration Kit] new streaming query for contract state events
- [Integration Kit] indexing contract keys for consuming exercise events
- Add prometheus metrics as a cli option in the participant integration api
[daml build] A new flag `--access-token-file` is added for the `daml
build` command. It allows the specify the path to an access token to
authenticate against the ledger API. This is needed if the project
depends on a remote Daml package hosted on such a ledger. Alternatively,
the path to the token can also be specified in the `daml.yaml` project
file under the `ledger.access-token-file` field.
[daml packages] A new `daml packages list` command has been added to
list packages deployed on a remote Daml ledger.
- Daml: (Early access) add support for BigNumeric
- [HTTP JSON API] Range queries within variant data would not return matching
  data when using the PostgreSQL backend with JSON API; this is fixed.
  See `issue #9321 <https://github.com/digital-asset/daml/pull/9321>`__.
```

CHANGELOG_BEGIN
CHANGELOG_END
cocreature added a commit that referenced this pull request May 20, 2021
Windows version of #9370 to further reduce queues until we either get
more nodes or find another solution. We still test everything in the
daily run.

changelog_begin
changelog_end
mergify bot pushed a commit that referenced this pull request May 20, 2021
* Disable per commit windows compat tests

Windows version of #9370 to further reduce queues until we either get
more nodes or find another solution. We still test everything in the
daily run.

changelog_begin
changelog_end

* remove success check for skipped windows compat

Co-authored-by: Gary Verhaegen <gary.verhaegen@digitalasset.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/daml-engine DAML-LF Engine & Interpreter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants