From b967ef3e6d3e4ec2f8b1858e9c9018da2d9259da Mon Sep 17 00:00:00 2001 From: Matthias Schmalz <36510334+matthiasS-da@users.noreply.github.com> Date: Tue, 21 Mar 2023 17:01:20 +0100 Subject: [PATCH] Revised ScalaDocs of DAML engine to make security relevant assumptions explicit (#16555) CHANGE_LOG_BEGIN CHANGE_LOG_END --- .../digitalasset/daml/lf/engine/Engine.scala | 51 +++++++++---------- .../digitalasset/daml/lf/engine/Result.scala | 32 +++++++++++- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala b/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala index b3abac9f01..11e3e5666f 100644 --- a/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala +++ b/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala @@ -75,31 +75,26 @@ class Engine(val config: EngineConfig = Engine.StableConfig) { def info = new EngineInfo(config) - /** Executes commands `cmds` under the authority of `submitters`, with additional readers `readAs`, - * and returns one of the following: + /** Interprets a sequence of commands `cmds` to the corresponding `SubmittedTransaction` and `Tx.Metadata`. + * Requests data required during the interpretation (such as the contract or package corresponding to a given id) + * through the `Result` subclasses. + * + * The resulting transaction (if any) meets the following properties: * * - * [[transactionSeed]] is the master hash used to derive node and contractId discriminator. - * If left undefined, no discriminator will be generated. - * - * This method does perform authorization checks - * - * The resulting transaction is annotated with packages required to validate it. + * @param submitters the parties authorizing the root actions (both read and write) of the resulting transaction + * ("committers" according to the ledger model) + * @param readAs the parties authorizing the root actions (only read, but no write) of the resulting transaction + * @param cmds the commands to be interpreted + * @param disclosures contracts to be used as input contracts of the transaction; + * contract data may come from an untrusted source and will therefore be validated during interpretation. + * @param participantId a unique identifier (of the underlying participant) used to derive node and contractId discriminators + * @param submissionSeed the master hash used to derive node and contractId discriminators */ def submit( submitters: Set[Party], @@ -130,19 +125,21 @@ class Engine(val config: EngineConfig = Engine.StableConfig) { } yield tx -> meta.copy(submissionSeed = Some(submissionSeed)) } - /** Behaves like `submit`, but it takes a single command argument. + /** Behaves like `submit`, but it takes a single `ReplayCommand` (instead of `ApiCommands`) as input. * It can be used to reinterpret partially an already interpreted transaction. * * If the command would fail with an unhandled exception, we return a transaction containing a * single rollback node. (This is achieving by compiling with `unsafeCompileForReinterpretation` * which wraps the command with a catch-everything exception handler.) * - * [[nodeSeed]] is the seed of the Create and Exercise node as generated during submission. - * If undefined the contract IDs are derive using V0 scheme. - * The value of [[nodeSeed]] does not matter for other kind of nodes. - * * The reinterpretation does not recompute the package dependencies, so the field `usedPackages` in the * `Tx.MetaData` component of the output is always set to `empty`. + * + * @param nodeSeed the seed of the root node as generated during submission. + * If undefined the contract IDs are derive using V0 scheme. + * The value does not matter for other kind of nodes. + * @param submissionTime the submission time used to compute contract IDs + * @param ledgerEffectiveTime the ledger effective time used as a result of `getTime` during reinterpretation */ def reinterpret( submitters: Set[Party], diff --git a/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Result.scala b/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Result.scala index c14b62254b..55fb1ae39b 100644 --- a/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Result.scala +++ b/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Result.scala @@ -69,11 +69,15 @@ sealed trait Result[+A] extends Product with Serializable { final case class ResultInterruption[A](continue: () => Result[A]) extends Result[A] +/** Indicates that the command (re)interpretation was successful. + */ final case class ResultDone[A](result: A) extends Result[A] object ResultDone { val Unit: ResultDone[Unit] = new ResultDone(()) } +/** Indicates that the command (re)interpretation has failed. + */ final case class ResultError(err: Error) extends Result[Nothing] object ResultError { def apply(packageError: Error.Package.Error): ResultError = @@ -93,8 +97,12 @@ object ResultError { * To resume the computation, the caller must invoke `resume` with the following argument: * + * + * The caller of `resume` has to ensure that the contract instance passed to `resume` is a contract instance that + * has previously been associated with `acoid` by the engine. + * The engine does not validate the given contract instance. */ final case class ResultNeedContract[A]( acoid: ContractId, @@ -105,17 +113,37 @@ final case class ResultNeedContract[A]( * To resume the computation, the caller must invoke `resume` with the following argument: * + * + * It depends on the engine configuration whether the engine will validate the package provided to `resume`. + * If validation is switched off, it is the callers responsibility to provide a valid package corresponding to `packageId`. */ final case class ResultNeedPackage[A](packageId: PackageId, resume: Option[Package] => Result[A]) extends Result[A] +/** Intermediate result indicating that the contract id corresponding to a key is required to complete the computation. + * To resume the computation, the caller must invoke `resume` with the following argument: + * + * + * The caller of `resume` has to ensure that any contract id passed to `resume` has previously been associated with + * a contract with `key` as a key. + * Other than that, the caller does not need to validate the data passed to `resume`. In particular, it may pass + * the id of an archived contract to `resume`. + * It may also provide `None` to `resume` when the `key` is actually assigned. + */ final case class ResultNeedKey[A]( key: GlobalKeyWithMaintainers, resume: Option[ContractId] => Result[A], ) extends Result[A] +/** TODO: https://github.com/digital-asset/daml/issues/15882 + * add ScalaDoc explaining the impact of the answers and the responsibilities of the caller. + * (Similarly as for the other subclasses of Result.) + */ final case class ResultNeedAuthority[A]( holding: Set[Party], requesting: Set[Party],