Add ledger API test tool tests for rollback projections (#9778)

* Add ledger API test tool tests for rollback projections

This adds 3 tests for projections under rollback nodes.

The first one is relatively clear hopefully and tests divulgence.

The other two are a bit more intricate. For both of those we can also
not test too much via the ledger API since we don’t actually get
access to rollback nodes. However, it still seems useful to at least
exercise those code paths and make sure they don’t do anything
horribbly wrong.

The second test tests the normalization rules from
https://github.com/digital-asset/daml/blob/main/docs/source/concepts/ledger-model/ledger-exceptions.rst#privacy

The last one tests a more complex structure with deeply nested
rollback nodes and different informees.

changelog_begin
changelog_end

* Update ledger/test-common/src/main/daml/semantic/Exceptions.daml

Co-authored-by: Sofia Faro <sofia.faro@digitalasset.com>

Co-authored-by: Sofia Faro <sofia.faro@digitalasset.com>
This commit is contained in:
Moritz Kiefer 2021-05-25 08:53:43 +02:00 committed by GitHub
parent fb6f72c81d
commit 1b428be7d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 295 additions and 17 deletions

View File

@ -8,7 +8,16 @@ import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.api.testtool.infrastructure.TransactionHelpers._
import com.daml.ledger.api.testtool.infrastructure.Synchronize.synchronize
import com.daml.ledger.test.semantic.Exceptions.{Divulger, ExceptionTester, Fetcher, WithKey}
import com.daml.ledger.test.semantic.Exceptions.{
Divulger,
ExceptionTester,
Fetcher,
WithSimpleKey,
Informer,
WithKey,
WithKeyDelegate,
RollbackNestingHelper,
}
import io.grpc.Status
final class ExceptionsIT extends LedgerTestSuite {
@ -114,7 +123,7 @@ final class ExceptionsIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
for {
t <- ledger.create(party, ExceptionTester(party))
withKey <- ledger.create(party, WithKey(party))
withKey <- ledger.create(party, WithSimpleKey(party))
_ <- ledger.exercise(party, t.exerciseRolledbackArchiveConsuming(_, withKey))
} yield ()
})
@ -126,7 +135,7 @@ final class ExceptionsIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
for {
t <- ledger.create(party, ExceptionTester(party))
withKey <- ledger.create(party, WithKey(party))
withKey <- ledger.create(party, WithSimpleKey(party))
_ <- ledger.exercise(party, t.exerciseRolledbackArchiveNonConsuming(_, withKey))
} yield ()
})
@ -150,7 +159,7 @@ final class ExceptionsIT extends LedgerTestSuite {
for {
t <- ledger.create(party, ExceptionTester(party))
_ <- ledger.exercise(party, t.exerciseDuplicateKey(_))
_ <- ledger.create(party, WithKey(party))
_ <- ledger.create(party, WithSimpleKey(party))
failure <- ledger.exercise(party, t.exerciseDuplicateKey(_)).mustFail("duplicate key")
} yield {
assertGrpcError(failure, Status.Code.ABORTED, "DuplicateKey")
@ -164,7 +173,7 @@ final class ExceptionsIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
for {
t <- ledger.create(party, ExceptionTester(party))
withKey <- ledger.create(party, WithKey(party))
withKey <- ledger.create(party, WithSimpleKey(party))
failure <- ledger.exercise(party, t.exerciseDuplicateKey(_)).mustFail("duplicate key")
_ = assertGrpcError(failure, Status.Code.ABORTED, "DuplicateKey")
_ <- ledger.exercise(party, withKey.exerciseArchive(_))
@ -179,7 +188,7 @@ final class ExceptionsIT extends LedgerTestSuite {
)(implicit ec => { case Participants(Participant(ledger, party)) =>
for {
t <- ledger.create(party, ExceptionTester(party))
withKey <- ledger.create(party, WithKey(party))
withKey <- ledger.create(party, WithSimpleKey(party))
_ <- ledger.exercise(party, t.exerciseFetchKey(_))
_ <- ledger.exercise(party, withKey.exerciseArchive(_))
failure <- ledger.exercise(party, t.exerciseFetchKey(_)).mustFail("couldn't find key")
@ -198,7 +207,7 @@ final class ExceptionsIT extends LedgerTestSuite {
t <- ledger.create(party, ExceptionTester(party))
failure <- ledger.exercise(party, t.exerciseFetchKey(_)).mustFail("contract not found")
_ = assertGrpcError(failure, Status.Code.INVALID_ARGUMENT, "couldn't find key")
_ <- ledger.create(party, WithKey(party))
_ <- ledger.create(party, WithSimpleKey(party))
_ <- ledger.exercise(party, t.exerciseFetchKey(_))
} yield ()
})
@ -230,7 +239,7 @@ final class ExceptionsIT extends LedgerTestSuite {
for {
divulger <- aLedger.create(aParty, Divulger(aParty, bParty))
fetcher <- bLedger.create(bParty, Fetcher(bParty, aParty))
t <- bLedger.create(bParty, WithKey(bParty))
t <- bLedger.create(bParty, WithSimpleKey(bParty))
_ <- synchronize(aLedger, bLedger)
fetchFailure <- aLedger
.exercise(aParty, fetcher.exerciseFetch(_, t))
@ -242,4 +251,96 @@ final class ExceptionsIT extends LedgerTestSuite {
.exercise(aParty, fetcher.exerciseFetch(_, t))
} yield ()
})
test(
"ExRollbackProjectionDivulgence",
"Fetch and fetchbykey in projection divulge",
allocate(SingleParty, SingleParty),
)(implicit ec => {
case Participants(Participant(aLedger, aParty), Participant(bLedger, bParty)) =>
for {
fetcher <- bLedger.create(aParty, Fetcher(aParty, bParty))
withKey0 <- bLedger.create(aParty, WithKey(aParty, 0, List.empty))
withKey1 <- bLedger.create(aParty, WithKey(aParty, 1, List.empty))
_ <- synchronize(aLedger, bLedger)
fetchFailure <- bLedger
.exercise(bParty, fetcher.exerciseFetch_(_, withKey0))
.mustFail("contract could not be found")
_ = assertGrpcError(fetchFailure, Status.Code.ABORTED, "Contract could not be found")
fetchFailure <- bLedger
.exercise(bParty, fetcher.exerciseFetch_(_, withKey1))
.mustFail("contract could not be found")
_ = assertGrpcError(fetchFailure, Status.Code.ABORTED, "Contract could not be found")
tester <- aLedger.create(aParty, ExceptionTester(aParty))
_ <- aLedger.exercise(aParty, tester.exerciseProjectionDivulgence(_, bParty, withKey0))
_ <- synchronize(aLedger, bLedger)
_ <- bLedger
.exercise(bParty, fetcher.exerciseFetch_(_, withKey0))
_ <- bLedger
.exercise(bParty, fetcher.exerciseFetch_(_, withKey1))
} yield ()
})
test(
"ExRollbackProjectionNormalization",
"Projection normalization is correctly applied",
allocate(SingleParty, SingleParty, SingleParty),
)(implicit ec => {
// We cannot test projection & normalization directly via the ledger API
// since rollback nodes are erased so this test only ensures
// that the code paths for this are exercised and do not
// throw errors.
case Participants(
Participant(aLedger, aParty),
Participant(bLedger, bParty),
Participant(cLedger, cParty),
) =>
for {
abInformer <- aLedger.create(aParty, Informer(aParty, List(bParty)))
acInformer <- aLedger.create(aParty, Informer(aParty, List(cParty)))
abcInformer <- aLedger.create(aParty, Informer(aParty, List(bParty, cParty)))
keyDelegate <- bLedger.create(bParty, WithKeyDelegate(aParty, bParty))
_ <- synchronize(aLedger, bLedger)
_ <- synchronize(aLedger, cLedger)
tester <- aLedger.create(aParty, ExceptionTester(aParty))
_ <- aLedger.exercise(
aParty,
tester.exerciseProjectionNormalization(
_,
bParty,
keyDelegate,
abInformer,
acInformer,
abcInformer,
),
)
} yield ()
})
test(
"ExRollbackProjectionNesting",
"Nested rollback nodes are handled properly",
allocate(SingleParty, SingleParty, SingleParty),
)(implicit ec => {
// We cannot test projection & normalization directly via the ledger API
// since rollback nodes are erased so this test only ensures
// that the code paths for this are exercised and do not
// throw errors.
case Participants(
Participant(aLedger, aParty),
Participant(bLedger, bParty),
Participant(cLedger, cParty),
) =>
for {
keyDelegate <- bLedger.create(bParty, WithKeyDelegate(aParty, bParty))
nestingHelper <- cLedger.create(cParty, RollbackNestingHelper(aParty, bParty, cParty))
_ <- synchronize(aLedger, bLedger)
_ <- synchronize(aLedger, cLedger)
tester <- aLedger.create(aParty, ExceptionTester(aParty))
_ <- aLedger.exercise(
aParty,
tester.exerciseProjectionNesting(_, bParty, keyDelegate, nestingHelper),
)
} yield ()
})
}

View File

@ -6,6 +6,7 @@ module Exceptions where
#ifdef DAML_EXCEPTIONS
import DA.Assert
import DA.Exception
exception E
@ -20,7 +21,7 @@ exception E2
where
message t
template WithKey
template WithSimpleKey
with
p : Party
where
@ -31,6 +32,22 @@ template WithKey
controller p
do pure ()
template WithKey
with
p : Party
v : Int
obs: [Party]
where
signatory p
observer obs
key (p, v) : (Party, Int)
maintainer key._1
choice Consume : ()
with
a : Party
controller a
do pure ()
template Divulger
with
sig : Party
@ -40,7 +57,7 @@ template Divulger
observer obs
nonconsuming choice Divulge : ()
with
cid : ContractId WithKey
cid : ContractId WithSimpleKey
controller obs
do try (fetch cid >> throw (E ""))
catch
@ -53,12 +70,82 @@ template Fetcher
where
signatory sig
observer obs
nonconsuming choice Fetch : WithKey
nonconsuming choice Fetch : WithSimpleKey
with
cid : ContractId WithSimpleKey
controller obs
do fetch cid
nonconsuming choice Fetch_ : WithKey
with
cid : ContractId WithKey
controller obs
do fetch cid
template ProjectionDivulgenceHelper
with
a : Party
b : Party
where
signatory a
observer b
choice DivulgeProjection : ()
with
cid : ContractId WithKey
controller a
do -- Both the fetch and the fetchByKey
-- will be divulged to B.
c <- fetch cid
c.v === 0
_ <- fetchByKey @WithKey (a, 1)
throw (E "")
template RollbackNestingHelper
with
a : Party
b : Party
c : Party
where
signatory c
observer a
choice Rollback : ()
controller a
do try do
create (WithSimpleKey a)
try do
exerciseByKey @WithKey (b, 1) (Consume a)
throw (E "")
catch
E{} -> pure ()
exerciseByKey @WithKey (b, 1) (Consume a)
catch
E{} -> pure ()
template WithKeyDelegate
with
a : Party
b : Party
where
signatory b
observer a
nonconsuming choice CreateWithKey : ContractId WithKey
with
v : Int
controller a
do create (WithKey b v [a])
template Informer
with
a : Party
obs : [Party]
where
signatory a
observer obs
nonconsuming choice Inform : ()
observer obs
controller a
do pure ()
template ExceptionTester
with
p : Party
@ -106,13 +193,13 @@ template ExceptionTester
E _ -> pure ()
nonconsuming choice DuplicateKey : ()
controller p
do try (create (WithKey p) >> throw (E ""))
do try (create (WithSimpleKey p) >> throw (E ""))
catch
E _ -> pure ()
nonconsuming choice FetchKey : ()
controller p
do try (fetchByKey @WithKey p >> throw (E ""))
do try (fetchByKey @WithSimpleKey p >> throw (E ""))
catch
E _ -> pure ()
@ -130,7 +217,7 @@ template ExceptionTester
nonconsuming choice RolledbackArchiveConsuming : ()
with
cid : ContractId WithKey
cid : ContractId WithSimpleKey
controller p
do try (archive cid >> throw (E ""))
catch
@ -138,7 +225,7 @@ template ExceptionTester
nonconsuming choice RolledbackArchiveNonConsuming : ()
with
cid : ContractId WithKey
cid : ContractId WithSimpleKey
controller p
do try (archive cid >> throw (E ""))
catch
@ -146,8 +233,98 @@ template ExceptionTester
nonconsuming choice RolledbackDuplicateKey : ()
controller p
do try (create (WithKey p) >> throw (E ""))
do try (create (WithSimpleKey p) >> throw (E ""))
catch
E _ -> create (WithKey p) >> pure ()
E _ -> create (WithSimpleKey p) >> pure ()
nonconsuming choice ProjectionDivulgence : ()
with
b : Party
keyCid : ContractId WithKey
controller p
do -- b sees all 3 childs of this exercise but not the exercise itself.
cid <- create (ProjectionDivulgenceHelper p b)
try (exercise cid (DivulgeProjection keyCid))
catch
E{} -> pure ()
archive cid
nonconsuming choice ProjectionNormalization : ()
with
b : Party
keyDelegate : ContractId WithKeyDelegate
pbInformer : ContractId Informer
pcInformer : ContractId Informer
pbcInformer : ContractId Informer
controller p
do -- Projection to B:
-- Normalization rule #3 folds R3 into R1
-- R2 and R3 must sit under R1 for key checks to pass
-- Projection to C:
-- Normalization rule #2 pulls R2 out of R1.
-- Normalization rule #1 drops R3.
try do -- R1
exercise keyDelegate (CreateWithKey 1) -- informees: p b
try do -- R2
exercise pbcInformer Inform -- informees: p b c
exerciseByKey @WithKey (b, 1) (Consume p) -- informees: p b
exercise keyDelegate (CreateWithKey 2) -- informees: p b
throw (E "")
catch
E{} -> pure ()
exercise pbInformer Inform -- informees: p b
try do -- R3
exerciseByKey @WithKey (b, 1) (Consume p) -- informees: p b
exercise keyDelegate (CreateWithKey 2) -- informees: p b
throw (E "")
catch
E{} -> pure()
exercise pcInformer Inform -- informees: p c
throw (E "")
catch
E{} -> pure ()
exercise keyDelegate (CreateWithKey 1)
pure ()
nonconsuming choice ProjectionNesting : ()
with
b : Party
keyDelegate : ContractId WithKeyDelegate
cid : ContractId RollbackNestingHelper
controller p
do exercise keyDelegate (CreateWithKey 1)
exercise self (ProjectionNestingInner b cid)
exerciseByKey @WithKey (b, 1) (Consume p)
pure ()
nonconsuming choice ProjectionNestingInner : ()
with
b : Party
cid : ContractId RollbackNestingHelper
controller p
do try do
_ <- create (Informer p [b])
try do
exerciseByKey @WithKey (b, 1) (Consume p)
throw (E "")
catch
E{} -> pure ()
exercise cid Rollback
throw (E "")
catch
E{} -> pure ()
#endif