clarify the role of party sets in various locations (#766)

* clarify role of party sets in various locations

* use 1.dev for tests...

...so that we're sure we're testing the latest value / tx versions.
This commit is contained in:
Francesco Mazzoli 2019-05-03 17:09:59 +02:00 committed by GitHub
parent 16ea1ceacd
commit e8261f2fd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 153 additions and 24 deletions

View File

@ -9,7 +9,7 @@ class EngineInfoTest extends WordSpec with Matchers {
EngineInfo.getClass.getSimpleName should {
"show supported LF, Transaction and Value versions" in {
EngineInfo.show shouldBe
"DAML LF Engine supports LF versions: 0, 0.dev, 1.0, 1.1, 1.2, 1.3, 1.dev; Transaction versions: 1, 2, 3, 4, 5; Value versions: 1, 2, 3, 4"
"DAML LF Engine supports LF versions: 0, 0.dev, 1.0, 1.1, 1.2, 1.3, 1.dev; Transaction versions: 1, 2, 3, 4, 5, 6; Value versions: 1, 2, 3, 4"
}
"toString returns the same value as show" in {

View File

@ -4,7 +4,7 @@
DAML-LF Transaction Specification
=================================
**version 5, 12 March 2019**
**version 6, 29 April 2019**
This specification, in concert with the ``transaction.proto``
machine-readable definition, defines a format for _transactions_, to be
@ -155,6 +155,8 @@ This table lists every version of this specification in ascending order
+--------------------+-----------------+
| 5 | 2019-03-12 |
+--------------------+-----------------+
| 6 | 2019-04-29 |
+--------------------+-----------------+
message Transaction
^^^^^^^^^^^^^^^^^^^
@ -300,6 +302,15 @@ As of version 1, these fields are included:
Every element of ``stakeholders`` is a party identifier.
``signatories`` must be a non-empty subset of ``stakeholders``.
.. note:: *This section is non-normative.*
The stakeholders of a contract are the signatories and the observers of
said contract.
The signatories of a contract are specified in the DAML-LF definition of
the template for said contract. Conceptually, they are the parties that
agreed for that contract to be created.
*since version 3*
A new field is included:
@ -314,6 +325,9 @@ A new field is included:
* Its ``key`` must conform to the key definition for the ``template_id``
in the ``contract_instance``.
The maintainers of a contract key are specified in the DAML-LF definition of
the template for the contract.
*since version 4*
``contract_id`` must not be set, and this new field is required:
@ -386,6 +400,12 @@ As of version 5, this new field is required to be non-empty:
Every element of ``actors`` is a party identifier.
.. note:: *This section is non-normative.*
Actors are specified explicitly by the user invoking fetching the
contract -- or in other words, they are _not_ a property of the
contract itself.
message NodeExercise
^^^^^^^^^^^^^^^^^^^^
@ -432,6 +452,17 @@ this choice.
Every element of ``actors``, ``stakeholders``, ``signatories``, and
``controllers`` is a party identifier.
.. note:: *This section is non-normative.*
The ``stakeholders`` and ``signatories`` field have the same meaning
they have for ``NodeCreate``.
The ``actors`` field contains the parties that exercised the choice.
The ``controllers`` field contains the parties that _can_ exercise
the choice. Note that according to the ledger model these two fields
_must_ be the same. For this reason the ``controllers`` field was
removed in version 6 -- see *since version 6* below.
*since version 4*
``contract_id`` must not be set, and this new field is required:
@ -449,6 +480,12 @@ If ``contract_id_struct``'s ``relative`` field is ``true``, then:
3. ``signatories`` must have the same elements as the corresponding
``NodeCreate``'s ``signatories`` field.
*since version 6*
The ``controllers`` field must be empty. Software needing to fill in
data structures that demand both actors and controllers must use
the ``actors`` field as the controllers.
message NodeLookupByKey
^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -306,7 +306,6 @@ object ValueGenerators {
chosenValue <- versionedValueGen
stakeholders <- genNonEmptyParties
signatories <- genNonEmptyParties
controllers <- genNonEmptyParties
children <- Gen
.listOf(Arbitrary.arbInt.arbitrary)
.map(_.map(Transaction.NodeId.unsafeFromIndex))
@ -322,7 +321,7 @@ object ValueGenerators {
chosenValue,
stakeholders,
signatories,
controllers,
actingParties,
children
)
}

View File

@ -10,6 +10,7 @@
// * 3: new node type NodeLookupByKey
// * 4: string contract_id replaced globally by ContractId message
// * 5: new field actors in NodeFetch
// * 6: removal of controllers in exercise nodes
syntax = "proto3";

View File

@ -81,6 +81,11 @@ object Node {
chosenValue: Val,
stakeholders: Set[Party],
signatories: Set[Party],
/** Note that here we have both actors and controllers because we use this
* data structure _before_ we validate the transaction. However for every
* valid transaction the actors must be the same as the controllers. This
* is why we removed the controllers field in transaction version 6.
*/
controllers: Set[Party],
children: ImmArray[Nid])
extends GenNode[Nid, Cid, Val] {
@ -95,7 +100,36 @@ object Node {
)
}
object NodeExercises extends WithTxValue3[NodeExercises]
object NodeExercises extends WithTxValue3[NodeExercises] {
/** After interpretation authorization, it must be the case that
* the controllers are the same as the acting parties. This
* apply method enforces it.
*/
def apply[Nid, Cid, Val](
targetCoid: Cid,
templateId: Identifier,
choiceId: ChoiceName,
optLocation: Option[Location],
consuming: Boolean,
actingParties: Set[Party],
chosenValue: Val,
stakeholders: Set[Party],
signatories: Set[Party],
children: ImmArray[Nid]): NodeExercises[Nid, Cid, Val] =
NodeExercises(
targetCoid,
templateId,
choiceId,
optLocation,
consuming,
actingParties,
chosenValue,
stakeholders,
signatories,
actingParties,
children)
}
final case class NodeLookupByKey[+Cid, +Val](
templateId: Identifier,

View File

@ -99,7 +99,7 @@ object TransactionCoder {
nodeId: Nid,
node: GenNode[Nid, Cid, Val]): Either[EncodeError, TransactionOuterClass.Node] = {
val nodeBuilder = TransactionOuterClass.Node.newBuilder().setNodeId(encodeNid(nodeId))
import TransactionVersions.minKeyOrLookupByKey
import TransactionVersions.{minKeyOrLookupByKey, minNoControllers}
node match {
case c: NodeCreate[Cid, Val] =>
encodeContractInstance(encodeVal, c.coinst).flatMap { inst =>
@ -151,7 +151,7 @@ object TransactionCoder {
}
case e: NodeExercises[Nid, Cid, Val] =>
encodeVal(e.chosenValue).map {
encodeVal(e.chosenValue).flatMap {
case (vversion, arg) =>
val exBuilder =
TransactionOuterClass.NodeExercise
@ -165,11 +165,21 @@ object TransactionCoder {
_.setContractIdStruct(_))
.addAllActors(e.actingParties.map(_.underlyingString).asJava)
.addAllChildren(e.children.map(encodeNid).toList.asJava)
.addAllControllers(e.controllers.map(_.underlyingString).asJava)
.addAllSignatories(e.signatories.map(_.underlyingString).asJava)
.addAllStakeholders(e.stakeholders.map(_.underlyingString).asJava)
nodeBuilder.setExercise(exBuilder).build()
if (transactionVersion precedes minNoControllers) {
if (e.controllers == e.actingParties) {
exBuilder.addAllControllers(e.controllers.map(_.underlyingString).asJava)
Right(nodeBuilder.setExercise(exBuilder).build())
} else {
Left(EncodeError(
s"As of version $transactionVersion, the controllers and actingParties of an exercise node _must_ be the same, but I got ${e.controllers} as controllers and ${e.actingParties} as actingParties."))
}
} else {
Right(nodeBuilder.setExercise(exBuilder).build())
}
}
case nlbk: NodeLookupByKey[Cid, Val] =>
@ -221,7 +231,7 @@ object TransactionCoder {
protoNode: TransactionOuterClass.Node): Either[DecodeError, (Nid, GenNode[Nid, Cid, Val])] = {
val nodeId = decodeNid(protoNode.getNodeId)
import TransactionVersions.minKeyOrLookupByKey
import TransactionVersions.{minKeyOrLookupByKey, minNoControllers}
protoNode.getNodeTypeCase match {
case NodeTypeCase.CREATE =>
for {
@ -274,10 +284,19 @@ object TransactionCoder {
children <- childrenOrError
cv <- decodeVal(protoExe.getChosenValue)
templateId <- ValueCoder.decodeIdentifier(protoExe.getTemplateId)
controllers <- toPartySet(protoExe.getControllersList)
actingParties <- toPartySet(protoExe.getActorsList)
encodedControllers <- toPartySet(protoExe.getControllersList)
controllers <- if (!(txVersion precedes minNoControllers)) {
if (encodedControllers.isEmpty) {
Right(actingParties)
} else {
Left(DecodeError(s"As of version $txVersion, exercise controllers must be empty."))
}
} else {
Right(encodedControllers)
}
signatories <- toPartySet(protoExe.getSignatoriesList)
stakeholders <- toPartySet(protoExe.getStakeholdersList)
actingParties <- toPartySet(protoExe.getActorsList)
} yield
(
ni,

View File

@ -15,12 +15,13 @@ final case class TransactionVersion(protoValue: String)
*/
object TransactionVersions
extends LfVersions(
maxVersion = TransactionVersion("5"),
previousVersions = List("1", "2", "3", "4") map TransactionVersion)(_.protoValue) {
maxVersion = TransactionVersion("6"),
previousVersions = List("1", "2", "3", "4", "5") map TransactionVersion)(_.protoValue) {
private[this] val minVersion = TransactionVersion("1")
private[transaction] val minKeyOrLookupByKey = TransactionVersion("3")
private[transaction] val minFetchActors = TransactionVersion("5")
private[transaction] val minNoControllers = TransactionVersion("6")
def assignVersion(a: GenTransaction[_, _, _ <: VersionedValue[_]]): TransactionVersion = {
require(a != null)

View File

@ -48,6 +48,7 @@ private[lf] object VersionTimeline {
This(That(TransactionVersion("5"))),
That(LanguageVersion(LMV.V1, "2")),
Both(This(ValueVersion("4")), LanguageVersion(LMV.V1, "3")),
This(That(TransactionVersion("6"))),
That(LanguageVersion(LMV.V1, Dev)),
// add new versions above this line
// do *not* backfill to make more Boths, because such would

View File

@ -11,9 +11,24 @@ import "com/digitalasset/ledger/api/v1/value.proto";
option java_outer_classname = "EventOuterClass";
option java_package = "com.digitalasset.ledger.api.v1";
// An event on the ledger can either be the creation or the archiving of a contract, or the exercise of a choice on a contract.
// The ``GetTransactionTrees`` response will only contain create and exercise events.
// Archive events correspond to consuming exercise events.
// An event on the ledger can either be the creation or the archiving of
// a contract, or the exercise of a choice on a contract.
//
// The ``GetTransactionTrees`` response will only contain create and
// exercise events. Archive events correspond to consuming exercise
// events.
//
// In the transaction service the events are restricted to the events
// visible for the parties specified in the transaction filter. Each
// event message type below contains a ``witness_parties`` field which
// indicates the subset of the requested parties that can see the event
// in question.
//
// However, note that transaction _trees_ might contain events with
// _no_ witness parties, which were included simply because they were
// children of events which have witnesses. On the other hand, on
// the flat transaction stream you'll only receive events that have
// witnesses.
message Event {
oneof event {
CreatedEvent created = 1;
@ -41,7 +56,11 @@ message CreatedEvent {
// Required
Record create_arguments = 4;
// The parties that are notified of this event.
// The parties that are notified of this event. For `CreatedEvent`s,
// these are the intersection of the stakeholders of the contract in
// question and the parties specified in the `TransactionFilter`. The
// stakeholders are the union of the signatories and the observers of
// the contract.
// Required
repeated string witness_parties = 5;
}
@ -61,7 +80,11 @@ message ArchivedEvent {
// Required
Identifier template_id = 3;
// The parties that are notified of this event.
// The parties that are notified of this event. For `ArchivedEvent`s,
// these are the intersection of the stakeholders of the contract in
// question and the parties specified in the `TransactionFilter`. The
// stakeholders are the union of the signatories and the observers of
// the contract.
// Required
repeated string witness_parties = 4;
}
@ -101,9 +124,21 @@ message ExercisedEvent {
// Required
bool consuming = 8;
// field ID 9 deprecated
reserved 9; // removed field
// The parties that are notified of this event.
// The parties that are notified of this event. The witnesses of an exercise
// node will depend on whether the exercise was consuming or not.
//
// If consuming, the witnesses are the union of the stakeholders and
// the actors.
//
// If not consuming, the witnesses are the union of the signatories and
// the actors. Note that the actors might not necessarily be observers
// and thus signatories. This is the case when the controllers of a
// choice are specified using "flexible controllers", using the
// `choice ... controller` syntax, and said controllers are not
// explicitly marked as observers.
//
// Required
repeated string witness_parties = 10;

View File

@ -118,7 +118,7 @@ genrule(
daml_compile(
name = "Test",
main_src = "src/test/resources/damls/Test.daml",
target = "1.3",
target = "1.dev",
visibility = ["//visibility:public"],
)

View File

@ -368,7 +368,10 @@ private class PostgresLedgerDao(
"recorded_at" -> recordedAt,
"transaction" -> transactionSerializer
.serialiseTransaction(transaction)
.getOrElse(sys.error(s"failed to serialise transaction! trId: ${transactionId}"))
.fold(
e => sys.error(s"failed to serialise transaction! trId: ${transactionId}: $e"),
identity
)
)
.execute()

View File

@ -323,7 +323,6 @@ class PostgresDaoSpec
VersionedValue(ValueVersions.acceptedVersions.head, ValueText("some choice value")),
Set(SimpleString.assertFromString("Alice"), SimpleString.assertFromString("Bob")),
Set(SimpleString.assertFromString("Alice"), SimpleString.assertFromString("Bob")),
Set(SimpleString.assertFromString("Alice"), SimpleString.assertFromString("Bob")),
ImmArray.empty
)),
ImmArray(s"event$id"),