Skip to content

Commit

Permalink
Fix contract key uniqueness check in kvutils (digital-asset#2933)
Browse files Browse the repository at this point in the history
* Fix contract key uniqueness check in kvutils

Archival of a contract with a key and recreation within the
same transaction is now allowed in kvutils.

* Add assertions to check that new contract has been created with same key
  • Loading branch information
Jussi Mäki authored and mziolekda committed Sep 17, 2019
1 parent a867736 commit 11f1735
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import java.util.UUID
import com.daml.ledger.api.testtool.infrastructure.{LedgerSession, LedgerTest, LedgerTestSuite}
import com.digitalasset.ledger.test_stable.DA.Types.Tuple2
import com.digitalasset.ledger.test_stable.Test.Delegation._
import com.digitalasset.ledger.test_stable.Test.Delegated._
import com.digitalasset.ledger.test_stable.Test.ShowDelegated._
import com.digitalasset.ledger.test_stable.Test.TextKey._
import com.digitalasset.ledger.test_stable.Test.TextKeyOperations._
import com.digitalasset.ledger.test_stable.Test._
import io.grpc.Status
import scalaz.Tag

final class ContractKeys(session: LedgerSession) extends LedgerTestSuite(session) {

Expand Down Expand Up @@ -162,10 +164,37 @@ final class ContractKeys(session: LedgerSession) extends LedgerTestSuite(session
}
}

val recreateContractKeys =
LedgerTest("CKRecreate", "Contract keys can be recreated in single transaction") { context =>
val key = s"${UUID.randomUUID.toString}-key"
for {
ledger <- context.participant()
owner <- ledger.allocateParty()
delegated1TxTree <- ledger
.submitAndWaitRequest(owner, Delegated(owner, key).create.command)
.flatMap(ledger.submitAndWaitForTransactionTree)
delegated1Id = com.digitalasset.ledger.client.binding.Primitive
.ContractId[Delegated](delegated1TxTree.eventsById.head._2.getCreated.contractId)

delegated2TxTree <- ledger.exercise(owner, delegated1Id.exerciseRecreate)
} yield {
assert(delegated2TxTree.eventsById.size == 2)
val event = delegated2TxTree.eventsById.filter(_._2.kind.isCreated).head._2
assert(
Tag.unwrap(delegated1Id) != event.getCreated.contractId,
"New contract was not created")
assert(
event.getCreated.contractKey == delegated1TxTree.eventsById.head._2.getCreated.contractKey,
"Contract keys did not match")

}
}

override val tests: Vector[LedgerTest] = Vector(
fetchDivulgedContract,
rejectFetchingUndisclosedContract,
processContractKeys
processContractKeys,
recreateContractKeys
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import com.digitalasset.daml.lf.archive.Reader.ParseError
import com.digitalasset.daml.lf.data.Ref.{PackageId, Party}
import com.digitalasset.daml.lf.data.Time.Timestamp
import com.digitalasset.daml.lf.engine.{Blinding, Engine}
import com.digitalasset.daml.lf.transaction.Node.{GlobalKey, NodeCreate}
import com.digitalasset.daml.lf.transaction.Node.{GlobalKey, NodeCreate, NodeExercises}
import com.digitalasset.daml.lf.transaction.{BlindingInfo, GenTransaction}
import com.digitalasset.daml.lf.value.Value.{AbsoluteContractId, ContractId, NodeId, VersionedValue}
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -157,21 +157,34 @@ private[kvutils] case class ProcessTransactionSubmission(
private def validateContractKeyUniqueness: Commit[Unit] =
for {
damlState <- getDamlState
allUnique = relTx.fold(GenTransaction.AnyOrder, true) {
case (allUnique, (_nodeId, create: NodeCreate[_, VersionedValue[ContractId]]))
if create.key.isDefined =>
val stateKey = Conversions.contractKeyToStateKey(
GlobalKey(
create.coinst.template,
Conversions.forceAbsoluteContractIds(create.key.get.key)))

allUnique &&
damlState
.get(stateKey)
.forall(!_.getContractKeyState.hasContractId)

case (allUnique, _) => allUnique
}
startingKeys = damlState.collect {
case (k, v) if k.hasContractKey && v.getContractKeyState.hasContractId => k
}.toSet

allUnique = relTx
.fold(GenTransaction.TopDown, (true, startingKeys)) {
case (
(allUnique, existingKeys),
(_nodeId, exe: NodeExercises[_, _, VersionedValue[ContractId]]))
if exe.key.isDefined && exe.consuming =>
val stateKey = Conversions.contractKeyToStateKey(
GlobalKey(exe.templateId, Conversions.forceAbsoluteContractIds(exe.key.get)))
(allUnique, existingKeys - stateKey)

case (
(allUnique, existingKeys),
(_nodeId, create: NodeCreate[_, VersionedValue[ContractId]]))
if create.key.isDefined =>
val stateKey = Conversions.contractKeyToStateKey(
GlobalKey(
create.coinst.template,
Conversions.forceAbsoluteContractIds(create.key.get.key)))

(allUnique && !existingKeys.contains(stateKey), existingKeys + stateKey)

case (accum, _) => accum
}
._1

r <- if (allUnique)
pass
Expand Down
6 changes: 6 additions & 0 deletions ledger/test-common/src/main/daml/Test.daml
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,11 @@ template Delegated
key (owner, k) : (Party, Text)
maintainer key._1

controller owner can
Recreate: () do
create this
return ()

template Delegation
with
owner : Party
Expand Down Expand Up @@ -455,6 +460,7 @@ template Delegation
actual <- lookupByKey @Delegated (p, k)
assertMsg "lookup matches" (expected == actual)


template ShowDelegated
with
owner: Party
Expand Down
1 change: 1 addition & 0 deletions unreleased.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ HEAD — ongoing
+ [DAML-LF] add CAST_NUMERIC and SHIFT_NUMERIC in DAML-LF 1.dev
+ [Sandbox] Dramatically increased performance of the ActiveContractService by only loading the contracts that the parties in the transaction filter are allowed to see.
+ [DAML-LF] change signature of MUL_NUMERIC and DIV_NUMERIC
+ [DAML Integration Kit] fix contract key uniqueness check in kvutils.

0 comments on commit 11f1735

Please sign in to comment.