Skip to content

Commit

Permalink
Divulgence crash tests [DPP-433] (#9942)
Browse files Browse the repository at this point in the history
* A test case for crashing participant in a multi-node env with divulged contracts

* Fixed test naming

CHANGELOG_BEGIN
CHANGELOG_END

* Additional usability check for the disclosed contract

* A newline

* Verify that fetching an archived contract is not possible

* Second test case

* A `ledger-api-test-tool` test case for divulging a contract with key twice on multiple participants [DPP-433] (#9970)

* A test case for divulging a contract with a key twice on multiple participants

CHANGELOG_BEGIN
CHANGELOG_END

* Use autogenerated archive method

* Trigger build

* Increased timeout for TransactionServiceIT:TXInvisibleTransactionTreeByEventId
  • Loading branch information
kamil-da authored Jun 29, 2021
1 parent bb46417 commit ef9a04c
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@
package com.daml.ledger.api.testtool.suites

import java.util.regex.Pattern

import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.Assertions._
import com.daml.ledger.api.testtool.infrastructure.Eventually.eventually
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.api.testtool.infrastructure.Synchronize.synchronize
import com.daml.ledger.api.testtool.infrastructure.TransactionHelpers._
import com.daml.ledger.api.v1.value.{Record, RecordField, Value}
import com.daml.ledger.client.binding.Primitive.ContractId
import com.daml.ledger.test.model.DA.Types.Tuple2
import com.daml.ledger.test.model.Test.Delegated._
import com.daml.ledger.test.model.Test.Delegation._
import com.daml.ledger.test.model.Test.ShowDelegated._
import com.daml.ledger.test.model.Test.TextKey._
import com.daml.ledger.test.model.Test.TextKeyOperations._
import com.daml.ledger.test.model.Test._
import com.daml.ledger.test.model.Test
import io.grpc.Status
import scalaz.Tag

Expand All @@ -28,6 +23,7 @@ final class ContractKeysIT extends LedgerTestSuite {
"Divulged contracts cannot be fetched or looked up by key by non-stakeholders",
allocate(SingleParty, SingleParty),
)(implicit ec => { case Participants(Participant(alpha, owner), Participant(beta, delegate)) =>
import Test.{Delegated, Delegation, ShowDelegated}
val key = alpha.nextKeyId()
for {
// create contracts to work with
Expand Down Expand Up @@ -70,6 +66,7 @@ final class ContractKeysIT extends LedgerTestSuite {
"Contract Keys should reject fetching an undisclosed contract",
allocate(SingleParty, SingleParty),
)(implicit ec => { case Participants(Participant(alpha, owner), Participant(beta, delegate)) =>
import Test.{Delegated, Delegation}
val key = alpha.nextKeyId()
for {
// create contracts to work with
Expand Down Expand Up @@ -116,6 +113,7 @@ final class ContractKeysIT extends LedgerTestSuite {
"Contract keys should be scoped by maintainer",
allocate(SingleParty, SingleParty),
)(implicit ec => { case Participants(Participant(alpha, alice), Participant(beta, bob)) =>
import Test.{TextKey, TextKeyOperations, MaintainerNotSignatory}
val key1 = alpha.nextKeyId()
val key2 = alpha.nextKeyId()
val unknownKey = alpha.nextKeyId()
Expand Down Expand Up @@ -200,6 +198,7 @@ final class ContractKeysIT extends LedgerTestSuite {

test("CKRecreate", "Contract keys can be recreated in single transaction", allocate(SingleParty))(
implicit ec => { case Participants(Participant(ledger, owner)) =>
import Test.Delegated
val key = ledger.nextKeyId()
for {
delegated1TxTree <- ledger
Expand Down Expand Up @@ -231,6 +230,7 @@ final class ContractKeysIT extends LedgerTestSuite {
"Contract keys created by transient contracts are properly archived",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, owner)) =>
import Test.{Delegated, Delegation}
val key = ledger.nextKeyId()
val key2 = ledger.nextKeyId()

Expand Down Expand Up @@ -258,6 +258,7 @@ final class ContractKeysIT extends LedgerTestSuite {
"The contract key should be exposed if the template specifies one",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import Test.TextKey
val expectedKey = ledger.nextKeyId()
for {
_ <- ledger.create(party, TextKey(party, expectedKey, List.empty))
Expand All @@ -280,6 +281,7 @@ final class ContractKeysIT extends LedgerTestSuite {
"Exercising by key should be possible only when the corresponding contract is available",
allocate(SingleParty),
)(implicit ec => { case Participants(Participant(ledger, party)) =>
import Test.TextKey
val keyString = ledger.nextKeyId()
val expectedKey = Value(
Value.Sum.Record(
Expand Down Expand Up @@ -338,6 +340,7 @@ final class ContractKeysIT extends LedgerTestSuite {
allocate(SingleParty, SingleParty),
)(implicit ec => {
case Participants(Participant(ledger1, party1), Participant(ledger2, party2)) =>
import Test.LocalKeyVisibilityOperations
for {
ops <- ledger1.create(party1, LocalKeyVisibilityOperations(party1, party2))
_ <- synchronize(ledger1, ledger2)
Expand All @@ -362,4 +365,86 @@ final class ContractKeysIT extends LedgerTestSuite {
assertGrpcError(failedFetch, Status.Code.INVALID_ARGUMENT, "not visible")
}
})

test(
"CKDisclosedContractKeyReusability",
"Subsequent disclosed contracts can use the same contract key",
allocate(SingleParty, SingleParty),
)(implicit ec => {
case Participants(Participant(ledger1, party1), Participant(ledger2, party2)) =>
import Test.{WithKey, WithKeyCreator, WithKeyFetcher}
for {
// Create a helper contract and exercise a choice creating and disclosing a WithKey contract
creator1 <- ledger1.create(party1, WithKeyCreator(party1, party2))
withKey1 <- ledger1.exerciseAndGetContract[WithKey](
party1,
creator1.exerciseWithKeyCreator_DiscloseCreate(_, party1),
)

// Verify that the withKey1 contract is usable by the party2
fetcher <- ledger1.create(party1, WithKeyFetcher(party1, party2))

_ <- synchronize(ledger1, ledger2)

_ <- ledger2.exercise(party2, fetcher.exerciseWithKeyFetcher_Fetch(_, withKey1))

// Archive the disclosed contract
_ <- ledger1.exercise(party1, withKey1.exerciseArchive(_))

_ <- synchronize(ledger1, ledger2)

// Verify that fetching the contract is no longer possible after it was archived
_ <- ledger2
.exercise(party2, fetcher.exerciseWithKeyFetcher_Fetch(_, withKey1))
.mustFail("fetching an archived contract")

// Repeat the same steps for the second time
creator2 <- ledger1.create(party1, WithKeyCreator(party1, party2))
_ <- ledger1.exerciseAndGetContract[WithKey](
party1,
creator2.exerciseWithKeyCreator_DiscloseCreate(_, party1),
)

// Synchronize to verify that the second participant is working
_ <- synchronize(ledger1, ledger2)
} yield ()
})
import scalaz.syntax.tag._
test(
"CKDisclosedContractKeyReusabilityAsSubmitter",
"Subsequent disclosed contracts can use the same contract key (disclosure because of submitting)",
allocate(SingleParty, SingleParty),
)(implicit ec => {
case Participants(Participant(ledger1, party1), Participant(ledger2, party2)) =>
import Test.{WithKey, WithKeyCreatorAlternative}
for {
// Create a helper contract and exercise a choice creating and disclosing a WithKey contract
creator1 <- ledger1.create(party1, WithKeyCreatorAlternative(party1, party2))

_ <- synchronize(ledger1, ledger2)

_ <- ledger2.exercise(
party2,
creator1.exerciseWithKeyCreatorAlternative_DiscloseCreate(_),
)

_ <- synchronize(ledger1, ledger2)

Seq(withKey1Event) <- ledger1.activeContractsByTemplateId(List(WithKey.id.unwrap), party1)
withKey1 = ContractId.apply[WithKey](withKey1Event.contractId)
// Archive the disclosed contract
_ <- ledger1.exercise(party1, withKey1.exerciseArchive(_))

_ <- synchronize(ledger1, ledger2)

// Repeat the same steps for the second time
_ <- ledger2.exercise(
party2,
creator1.exerciseWithKeyCreatorAlternative_DiscloseCreate(_),
)

_ <- synchronize(ledger1, ledger2)
} yield ()
})

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ package com.daml.ledger.api.testtool.suites

import com.daml.ledger.api.testtool.infrastructure.Allocation._
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
import com.daml.ledger.api.testtool.infrastructure.Synchronize.waitForContract
import com.daml.ledger.test.model.Test.Divulgence2._
import com.daml.ledger.test.model.Test.Proposal._
import com.daml.ledger.test.model.Test.{Asset, Divulgence1, Divulgence2, Proposal}
import com.daml.ledger.api.testtool.infrastructure.Synchronize.{synchronize, waitForContract}
import com.daml.ledger.test.model.Test
import scalaz.Tag

final class DivulgenceIT extends LedgerTestSuite {
Expand All @@ -17,6 +15,7 @@ final class DivulgenceIT extends LedgerTestSuite {
"Divulged contracts should not be exposed by the transaction service",
allocate(TwoParties),
)(implicit ec => { case Participants(Participant(ledger, alice, bob)) =>
import Test.{Divulgence1, Divulgence2}
for {
divulgence1 <- ledger.create(alice, Divulgence1(alice))
divulgence2 <- ledger.create(bob, Divulgence2(bob, alice))
Expand Down Expand Up @@ -157,6 +156,7 @@ final class DivulgenceIT extends LedgerTestSuite {
"Divulged contracts should not be exposed by the active contract service",
allocate(TwoParties),
)(implicit ec => { case Participants(Participant(ledger, alice, bob)) =>
import Test.{Divulgence1, Divulgence2}
for {
divulgence1 <- ledger.create(alice, Divulgence1(alice))
divulgence2 <- ledger.create(bob, Divulgence2(bob, alice))
Expand Down Expand Up @@ -214,6 +214,7 @@ final class DivulgenceIT extends LedgerTestSuite {
"Divulgence should behave as expected in a workflow involving keys",
allocate(SingleParty, SingleParty),
)(implicit ec => { case Participants(Participant(alpha, proposer), Participant(beta, owner)) =>
import Test.{Asset, Proposal}
for {
offer <- alpha.create(proposer, Proposal(from = proposer, to = owner))
asset <- beta.create(owner, Asset(issuer = owner, owner = owner))
Expand All @@ -223,4 +224,43 @@ final class DivulgenceIT extends LedgerTestSuite {
// nothing to test, if the workflow ends successfully the test is considered successful
}
})

test(
"DivulgenceDivulgeAfterArchival",
"Divulging, archiving and divulging again a contract with key should be possible",
allocate(SingleParty, SingleParty),
)(implicit ec => { case Participants(Participant(alpha, partyA), Participant(beta, partyB)) =>
import Test.{WithKey, WithKeyDivulgenceHelper}
for {
// Create a helper contract where partyB is a signatory
helper <- beta.create(
partyB,
WithKeyDivulgenceHelper(divulgedTo = partyB, withKeyOwner = partyA),
)

_ <- synchronize(alpha, beta)

// Create a WithKey contract owned by the partyA
withKey1 <- alpha.create(partyA, WithKey(partyA))

// Divulge the withKey1 contract
_ <- alpha.exercise(partyA, helper.exerciseWithKeyDivulgenceHelper_Fetch(_, withKey1))

_ <- synchronize(alpha, beta)

// Archive the withKey1 contract
_ <- alpha.exercise(partyA, withKey1.exerciseArchive(_))

_ <- synchronize(alpha, beta)

// Create another WithKey contract with the same key as previously
withKey2 <- alpha.create(partyA, WithKey(partyA))

// Divulge the withKey2 contract
_ <- alpha.exercise(partyA, helper.exerciseWithKeyDivulgenceHelper_Fetch(_, withKey2))

// Synchronize to make sure that both participant are functional
_ <- synchronize(alpha, beta)
} yield ()
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,7 @@ class TransactionServiceIT extends LedgerTestSuite {
"TXInvisibleTransactionTreeByEventId",
"Do not expose an invisible transaction tree by event identifier",
allocate(SingleParty, SingleParty),
timeoutScale = 2.0,
)(implicit ec => { case Participants(Participant(alpha, party), Participant(beta, intruder)) =>
for {
dummy <- alpha.create(party, Dummy(party))
Expand Down
61 changes: 61 additions & 0 deletions ledger/test-common/src/main/daml/model/Test.daml
Original file line number Diff line number Diff line change
Expand Up @@ -682,3 +682,64 @@ template MultiPartyContract
do
actualCid <- lookupByKey @MultiPartyContract keyToFetch
assertMsg "LookupOtherByKey value matches" (expectedCid == actualCid)

template WithKey
with
p : Party
where
signatory p
key p : Party
maintainer key

template WithKeyCreator
with
p1 : Party
p2 : Party
where
signatory p1
observer p2
choice WithKeyCreator_DiscloseCreate : ContractId WithKey
with
p : Party
controller p
do create (WithKey p1)

template WithKeyCreatorAlternative
with
p1 : Party
p2 : Party
where
signatory p1
observer p2
nonconsuming choice WithKeyCreatorAlternative_DiscloseCreate : ContractId WithKey
controller p2
do create (WithKey p1)

template WithKeyFetcher
with
p1 : Party
p2 : Party
where
signatory p1
observer p2
choice WithKeyFetcher_Fetch : WithKey
with
contractId : ContractId WithKey
controller p2
do fetch contractId


template WithKeyDivulgenceHelper
with
divulgedTo : Party
withKeyOwner : Party
where
signatory divulgedTo

controller withKeyOwner can
nonconsuming WithKeyDivulgenceHelper_Fetch: ()
with
withKeyToFetch: ContractId WithKey
do
_ <- fetch withKeyToFetch
return ()

0 comments on commit ef9a04c

Please sign in to comment.