-
Notifications
You must be signed in to change notification settings - Fork 205
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
Add conformance test for deeply nested values #10319
Merged
Merged
Changes from 13 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
c8858c3
LF: Add conformance test of value nesting
remyhaemmerle-da ad886de
add comments
remyhaemmerle-da 943c69b
fix
remyhaemmerle-da 69d8a47
address Moritz's review
remyhaemmerle-da 3a36130
cosmetic
remyhaemmerle-da f234b72
cosmetic
remyhaemmerle-da 0b4fee5
Address Marcin's review
remyhaemmerle-da e58d392
add test for command with deeply nested value
remyhaemmerle-da 02e97c3
change `ConstructThenDestruct` to be sure it fail when calling the
remyhaemmerle-da 801a28a
add tests for CreatAndExercise command
remyhaemmerle-da 9089b7e
cosmetic
remyhaemmerle-da 906e18a
We use nesting and not depth.
remyhaemmerle-da d970ec7
add conformant tests for sandbox
remyhaemmerle-da 1560cd9
sandbox handle nesting up to 46
remyhaemmerle-da 87d59a3
do not try to get the transaction tree.
remyhaemmerle-da File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
172 changes: 172 additions & 0 deletions
172
...pi-test-tool/src/main/scala/com/daml/ledger/api/testtool/suites/DeeplyNestedValueIT.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package com.daml.ledger.api.testtool.suites | ||
|
||
import com.daml.ledger.api.refinements.ApiTypes.Party | ||
import com.daml.ledger.api.testtool.infrastructure.Allocation._ | ||
import com.daml.ledger.api.testtool.infrastructure.Assertions._ | ||
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite | ||
import com.daml.ledger.api.testtool.infrastructure.participant.ParticipantTestContext | ||
import com.daml.ledger.test.semantic.ValueNesting._ | ||
import io.grpc.Status | ||
|
||
import scala.annotation.tailrec | ||
import scala.concurrent.{ExecutionContext, Future} | ||
import scala.util.Success | ||
|
||
final class DeeplyNestedValueIT extends LedgerTestSuite { | ||
|
||
@tailrec | ||
private[this] def toNat(i: Long, acc: Nat = Nat.Z(())): Nat = | ||
if (i == 0) acc else toNat(i - 1, Nat.S(acc)) | ||
|
||
private[this] def toEither[X](future: Future[X])(implicit | ||
ec: ExecutionContext | ||
): Future[Either[Throwable, X]] = | ||
future.transform(x => Success(x.toEither)) | ||
|
||
private[this] def camlCase(s: String) = | ||
s.split(" ").iterator.map(_.capitalize).mkString("") | ||
|
||
List[Long](30, 100, 101, 200).foreach { nesting => | ||
val accepted = nesting <= 100 | ||
val result = if (accepted) "Accept" else "Reject" | ||
|
||
// Once converted to Nat, `n` will have nesting `nesting`. | ||
// Note that Nat.Z(()) has depth 2. | ||
val n = nesting - 1 | ||
|
||
// Choice argument are always wrapped in a record | ||
val nChoiceArgument = n - 1 | ||
|
||
// The nesting of the payload of a `Contract` is one more than the nat it contains | ||
val nContract = n - 1 | ||
|
||
// The nesting of the key of a `ContractWithKey` is one more than the nat it contains | ||
val nKey = n - 1 | ||
|
||
def test[T](description: String)( | ||
update: ExecutionContext => ( | ||
ParticipantTestContext, | ||
Party, | ||
) => Future[Either[Throwable, T]] | ||
) = | ||
super.test( | ||
result + camlCase(description) + nesting.toString, | ||
s"${result.toLowerCase}s $description with nesting of $nesting", | ||
allocate(SingleParty), | ||
)(implicit ec => { case Participants(Participant(alpha, party)) => | ||
update(ec)(alpha, party).map { | ||
case Right(_) if accepted => () | ||
case Left(err: Throwable) if !accepted => | ||
assertGrpcError(err, Status.Code.INVALID_ARGUMENT, None) | ||
case otherwise => | ||
fail("Unexpected " + otherwise.fold(err => s"failure: $err", _ => "success")) | ||
} | ||
}) | ||
|
||
test("create command") { implicit ec => (alpha, party) => | ||
toEither(alpha.create(party, Contract(party, nContract, toNat(nContract)))) | ||
} | ||
|
||
test("exercise command") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- toEither( | ||
alpha.exercise(party, handler.exerciseDestruct(_, toNat(nChoiceArgument))) | ||
) | ||
} yield result | ||
} | ||
|
||
test("create argument in CreateAndExercise command") { implicit ec => (alpha, party) => | ||
toEither( | ||
alpha | ||
.submitAndWaitForTransactionTree( | ||
alpha | ||
.submitAndWaitRequest( | ||
party, | ||
Contract(party, nContract, toNat(nContract)).createAnd | ||
.exerciseArchive(party) | ||
.command, | ||
) | ||
) | ||
) | ||
} | ||
|
||
test("choice argument in CreateAndExercise command") { implicit ec => (alpha, party) => | ||
toEither( | ||
alpha | ||
.submitAndWaitForTransactionTree( | ||
alpha | ||
.submitAndWaitRequest( | ||
party, | ||
Handler(party).createAnd.exerciseDestruct(party, toNat(nChoiceArgument)).command, | ||
) | ||
) | ||
) | ||
} | ||
|
||
test("exercise argument") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- toEither( | ||
alpha.exercise(party, handler.exerciseConstructThenDestruct(_, nChoiceArgument)) | ||
) | ||
} yield result | ||
} | ||
|
||
test("exercise output") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- toEither(alpha.exercise(party, handler.exerciseConstruct(_, n))) | ||
} yield result | ||
} | ||
|
||
test("create argument") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- toEither(alpha.exercise(party, handler.exerciseCreate(_, nContract))) | ||
} yield result | ||
} | ||
|
||
test("contract key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- toEither(alpha.exercise(party, handler.exerciseCreateKey(_, nKey))) | ||
} yield result | ||
} | ||
|
||
if (accepted) { | ||
// Because we cannot create contracts with nesting > 100, | ||
// it does not make sense to test fetch of those kinds of contracts. | ||
test("fetch by key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
_ <- alpha.exercise(party, handler.exerciseCreateKey(_, nKey)) | ||
result <- toEither(alpha.exercise(party, handler.exerciseFetchByKey(_, nKey))) | ||
} yield result | ||
} | ||
} | ||
|
||
test("failing lookup by key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
result <- toEither(alpha.exercise(party, handler.exerciseLookupByKey(_, nKey))) | ||
} yield result | ||
} | ||
|
||
if (accepted) { | ||
// Because we cannot create contracts with key nesting > 100, | ||
// it does not make sens to test successful lookup for those keys. | ||
test("successful lookup by key") { implicit ec => (alpha, party) => | ||
for { | ||
handler <- alpha.create(party, Handler(party)) | ||
_ <- alpha.exercise(party, handler.exerciseCreateKey(_, nKey)) | ||
result <- toEither(alpha.exercise(party, handler.exerciseLookupByKey(_, nKey))) | ||
} yield result | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
ledger/test-common/src/main/daml/semantic/ValueNesting.daml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
-- Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. | ||
-- SPDX-License-Identifier: Apache-2.0 | ||
|
||
module ValueNesting where | ||
|
||
data Nat = Z | S Nat | ||
deriving (Eq, Show) | ||
|
||
construct: Int -> Nat -> Nat | ||
construct x acc | x <= 0 = acc | ||
construct x acc = construct (x-1) (S acc) | ||
|
||
toNat : Int -> Nat | ||
toNat x = construct x Z | ||
|
||
destruct: Nat -> Int -> Int | ||
destruct Z acc = acc | ||
destruct (S n) acc = destruct n (acc + 1) | ||
|
||
toInt: Nat -> Int | ||
toInt x = destruct x 0 | ||
|
||
template Contract | ||
with | ||
party: Party | ||
i: Int | ||
n: Nat | ||
where | ||
signatory party | ||
|
||
template ContractWithKey | ||
with | ||
party: Party | ||
i: Int | ||
where | ||
signatory party | ||
key (party, toNat i): (Party, Nat) | ||
maintainer key._1 | ||
|
||
template Handler | ||
with | ||
party : Party | ||
where | ||
signatory party | ||
controller party can | ||
nonconsuming Construct : Nat | ||
with i : Int | ||
do | ||
pure $ toNat i | ||
nonconsuming Destruct : Int | ||
with n: Nat | ||
do pure $ toInt n | ||
nonconsuming ConstructThenDestruct : Int | ||
with | ||
i: Int | ||
do | ||
exercise self Destruct with n = toNat i | ||
nonconsuming Create: ContractId Contract | ||
with | ||
i : Int | ||
do | ||
create Contract with | ||
party = party | ||
i = i | ||
n = toNat i | ||
nonconsuming CreateKey: ContractId ContractWithKey | ||
with | ||
i : Int | ||
do | ||
create ContractWithKey with | ||
party = party | ||
i = i | ||
nonconsuming Fetch: Contract | ||
with cid: ContractId Contract | ||
do | ||
fetch cid | ||
nonconsuming FetchByKey: ContractId ContractWithKey | ||
with | ||
i: Int | ||
do | ||
(cid, _) <- fetchByKey @ContractWithKey (party, toNat i) | ||
pure cid | ||
nonconsuming LookupByKey: Optional (ContractId ContractWithKey) | ||
with | ||
i: Int | ||
do | ||
lookupByKey @ContractWithKey (party, toNat i) | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we also need a test case where the exercise argument itself is a
Nat
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That’s
Destruct
in line 50There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ConstructThenDestruct
test the case where the exercise argument itself is aNat
.Note that in my original design all the
Nat
s were built by evaluation and not coming from the ledger API.This is because, I am mainly concerned by the depth of values in the output transactions and trust the check of value depth in the command processing.
Though, I added in e58d392 one test for create command and one for exercise command that contain deeply nested value, as it is pretty straightforward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
801a28a adds 2 tests for
CreateAndExercise
command