Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix disclosure bug in the presence of upgrades #19167

Merged
merged 4 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/daml-script/converter/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ da_scala_library(
"//daml-lf/data",
"//daml-lf/interpreter",
"//daml-lf/language",
"//daml-lf/stable-packages",
"//daml-lf/transaction",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@

package com.daml.script.converter

import java.util
import scala.jdk.CollectionConverters._
import scalaz.syntax.bind._
import scalaz.std.either._
import com.daml.lf.data.{ImmArray, Ref}
import Ref._
import com.daml.lf.data.ImmArray
import com.daml.lf.data.Ref._
import com.daml.lf.language.Ast
import com.daml.lf.speedy.SValue._
import com.daml.lf.speedy.{ArrayList, SValue}
import SValue._
import com.daml.lf.stablepackages.StablePackagesV2
import com.daml.lf.value.Value.ContractId
import scalaz.std.either._
import scalaz.syntax.bind._

import java.util
import scala.jdk.CollectionConverters._

class ConverterException(message: String) extends RuntimeException(message)

Expand All @@ -35,6 +37,12 @@ private[daml] object Converter {
SRecord(ty, fieldNames, args) // TODO: construct SRecord directly from Map
}

def makeTuple(v1: SValue, v2: SValue): SValue =
record(StablePackagesV2.Tuple2, ("_1", v1), ("_2", v2))

def makeTuple(v1: SValue, v2: SValue, v3: SValue): SValue =
record(StablePackagesV2.Tuple3, ("_1", v1), ("_2", v2), ("_3", v3))

/** Unpack one step of a Pure/Roll-style free monad representation,
* with the assumption that `f` is a variant type.
*/
Expand Down
4 changes: 2 additions & 2 deletions sdk/daml-script/daml/Daml/Script.daml
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ data QueryDisclosurePayload a = QueryDisclosurePayload
{ parties : [Party]
, tplId : TemplateTypeRep
, cid : ContractId ()
, continue : Optional Text -> a
, continue : Optional (TemplateTypeRep, Text) -> a
, locations : [(Text, SrcLoc)]
} deriving Functor

Expand All @@ -317,7 +317,7 @@ queryDisclosure p c = lift $ Free $ QueryDisclosure QueryDisclosurePayload with
parties = toParties p
tplId = templateTypeRep @t
cid = cid
continue = pure . fmap \blob -> Disclosure tplId cid blob
continue = pure . fmap \(actualTplId, blob) -> Disclosure actualTplId cid blob
locations = getCallStack callStack
where
tplId = templateTypeRep @t
Expand Down
13 changes: 8 additions & 5 deletions sdk/daml-script/daml3/Daml/Script/Internal/Questions/Query.daml
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,23 @@ data QueryContractId = QueryContractId with
parties : [Party]
tplId : TemplateTypeRep
cid : ContractId ()
instance IsQuestion QueryContractId (Optional (AnyTemplate, Text)) where command = "QueryContractId"
instance IsQuestion QueryContractId (Optional (AnyTemplate, TemplateTypeRep, Text)) where command = "QueryContractId"

-- | Query for the contract with the given contract id.
--
-- Returns `None` if there is no active contract the party is a stakeholder on.
-- Otherwise returns a triplet (anyTemplate, templateId, blob) where anyTemplate
-- is the contract upgraded or downgraded to `t`, templateId is the ID of the
-- template as stored in the ledger (may be different from `t`), and blob is the
-- disclosure of the template as stored in the ledger (of type templateId).
--
-- WARNING: Over the gRPC and with the JSON API
-- in-memory backend this performs a linear search so only use this if the number of
-- active contracts is small.
--
-- This is semantically equivalent to calling `query`
-- and filtering on the client side.
queryContractId_ : forall t p. (Template t, IsParties p) => HasCallStack => p -> ContractId t -> Script (Optional (AnyTemplate, Text))
queryContractId_ : forall t p. (Template t, IsParties p) => HasCallStack => p -> ContractId t -> Script (Optional (AnyTemplate, TemplateTypeRep, Text))
queryContractId_ p c = lift $ QueryContractId with
parties = toParties p
tplId = templateTypeRep @t
Expand All @@ -58,14 +62,13 @@ queryContractId_ p c = lift $ QueryContractId with
-- convert = fmap $ fromSome . fromAnyTemplate

queryContractId: forall t p. (Template t, HasEnsure t, IsParties p) => HasCallStack => p -> ContractId t -> Script (Optional t)
queryContractId p c = fmap (fmap $ fromSome . fromAnyTemplate . fst) $ queryContractId_ p c
queryContractId p c = fmap (fmap $ \(anyTpl, _, _) -> fromSome (fromAnyTemplate anyTpl)) $ queryContractId_ p c

-- TODO https://github.com/digital-asset/daml/issues/17755
-- clean the API for different query function
queryDisclosure: forall t p. (Template t, IsParties p) => HasCallStack => p -> ContractId t -> Script (Optional Disclosure)
queryDisclosure p c = fmap (fmap $ \(_, blob) -> Disclosure tplId cid blob) $ queryContractId_ p c
queryDisclosure p c = fmap (fmap $ \(_, tplId, blob) -> Disclosure tplId cid blob) $ queryContractId_ p c
where
tplId = templateTypeRep @t
cid = coerceContractId c

data QueryInterface = QueryInterface with
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,14 @@ abstract class ConverterMethods(stablePackages: StablePackages) {
)
}

private[lf] def fromTemplateTypeRep(templateId: SValue): SValue =
record(stablePackages.TemplateTypeRep, ("getTemplateTypeRep", templateId))

private[lf] def fromTemplateTypeRep(templateId: value.Identifier): SValue =
record(stablePackages.TemplateTypeRep, ("getTemplateTypeRep", fromIdentifier(templateId)))
fromTemplateTypeRep(fromIdentifier(templateId))

private[lf] def fromTemplateTypeRep(templateId: Identifier): SValue =
fromTemplateTypeRep(STypeRep(TTyCon(templateId)))

private[lf] def fromAnyContractId(
scriptIds: ScriptIds,
Expand Down Expand Up @@ -246,10 +252,7 @@ abstract class ConverterMethods(stablePackages: StablePackages) {
("getAnyContractKey", SAny(key.ty, key.key)),
(
"getAnyContractKeyTemplateTypeRep",
record(
stablePackages.TemplateTypeRep,
("getTemplateTypeRep", STypeRep(TTyCon(key.templateId))),
),
fromTemplateTypeRep(key.templateId),
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,9 @@ object Converter extends script.ConverterMethods(StablePackagesV2) {
): Either[String, SValue] = {
for {
anyTpl <- fromContract(translator, contract)
} yield record(
StablePackagesV2.Tuple2,
("_1", SContractId(contract.contractId)),
("_2", anyTpl),
} yield makeTuple(
SContractId(contract.contractId),
anyTpl,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,29 @@ package engine
package script
package v1

import java.time.Clock
import org.apache.pekko.stream.Materializer
import com.daml.grpc.adapter.ExecutionSequencerFactory
import com.digitalasset.canton.ledger.api.domain.{User, UserRight}
import com.daml.lf.data.FrontStack
import com.daml.lf.{CompiledPackages, command}
import com.daml.lf.engine.preprocessing.ValueTranslator
import com.daml.lf.data.Ref.{Identifier, Name, PackageId, Party, UserId}
import com.daml.lf.data.Ref._
import com.daml.lf.data.Time.Timestamp
import com.daml.lf.language.{Ast}
import com.daml.lf.speedy.SExpr.{SEAppAtomic, SEValue}
import com.daml.lf.speedy.{ArrayList, SError, SValue}
import com.daml.lf.speedy.SExpr.SExpr
import com.daml.lf.engine.preprocessing.ValueTranslator
import com.daml.lf.language.Ast
import com.daml.lf.speedy.SExpr.{SEAppAtomic, SEValue, SExpr}
import com.daml.lf.speedy.SValue._
import com.daml.lf.speedy.Speedy.PureMachine
import com.daml.lf.stablepackages.StablePackagesV2
import com.daml.lf.speedy.{ArrayList, SError, SValue}
import com.daml.lf.value.Value
import com.daml.lf.value.Value.ContractId
import scalaz.{Foldable, OneAnd}
import scalaz.syntax.traverse._
import com.daml.lf.{CompiledPackages, command}
import com.daml.script.converter.Converter.{makeTuple, toContractId, toText}
import com.digitalasset.canton.ledger.api.domain.{User, UserRight}
import org.apache.pekko.stream.Materializer
import scalaz.std.either._
import scalaz.std.list._
import scalaz.std.option._
import com.daml.script.converter.Converter.{toContractId, toText}
import scalaz.syntax.traverse._
import scalaz.{Foldable, OneAnd}

import java.time.Clock
import scala.concurrent.{ExecutionContext, Future}

sealed trait ScriptF
Expand Down Expand Up @@ -231,6 +229,7 @@ object ScriptF {
} yield SEAppAtomic(SEValue(continue), Array(SEValue(SList(res))))

}

final case class QueryContractId(
parties: OneAnd[Set, Party],
tplId: Identifier,
Expand All @@ -251,7 +250,14 @@ object ScriptF {
optR <- client.queryContractId(parties, tplId, cid)
optR <- Converter.toFuture(
if (asDisclosure)
Right(optR.map(c => SValue.SText(c.blob.toHexString)))
Right(
optR.map(c =>
makeTuple(
Converter.fromTemplateTypeRep(c.templateId),
SValue.SText(c.blob.toHexString),
)
)
)
else
optR.traverse(Converter.fromContract(env.valueTranslator, _))
)
Expand All @@ -272,11 +278,6 @@ object ScriptF {
esf: ExecutionSequencerFactory,
): Future[SExpr] = {

def makePair(v1: SValue, v2: SValue): SValue = {
import com.daml.script.converter.Converter.record
record(StablePackagesV2.Tuple2, ("_1", v1), ("_2", v2))
}

for {
viewType <- Converter.toFuture(env.lookupInterfaceViewTy(interfaceId))
client <- Converter.toFuture(env.clients.getPartiesParticipant(parties))
Expand All @@ -287,7 +288,7 @@ object ScriptF {
.traverse { case (cid, optView) =>
optView match {
case None =>
Right(makePair(SContractId(cid), SOptional(None)))
Right(makeTuple(SContractId(cid), SOptional(None)))
case Some(view) =>
for {
view <- Converter.fromInterfaceView(
Expand All @@ -296,7 +297,7 @@ object ScriptF {
view,
)
} yield {
makePair(SContractId(cid), SOptional(Some(view)))
makeTuple(SContractId(cid), SOptional(Some(view)))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,26 +221,23 @@ object Converter extends script.ConverterMethods(StablePackagesV2) {
)
.map { xs => SList(xs.to(FrontStack)) }

// Convert an active contract to AnyTemplate
def fromContract(
translator: preprocessing.ValueTranslator,
contract: ScriptLedgerClient.ActiveContract,
enableContractUpgrading: Boolean = false,
): Either[String, SValue] =
fromAnyTemplate(translator, contract.templateId, contract.argument, enableContractUpgrading)

// Convert a Created event to a pair of (ContractId (), AnyTemplate)
def fromCreated(
translator: preprocessing.ValueTranslator,
contract: ScriptLedgerClient.ActiveContract,
targetTemplateId: Identifier,
enableContractUpgrading: Boolean = false,
): Either[String, SValue] = {
for {
anyTpl <- fromContract(translator, contract, enableContractUpgrading)
} yield record(
StablePackagesV2.Tuple2,
("_1", SContractId(contract.contractId)),
("_2", anyTpl),
anyTpl <- fromAnyTemplate(
translator,
targetTemplateId,
contract.argument,
enableContractUpgrading,
)
} yield makeTuple(
SContractId(contract.contractId),
anyTpl,
)
}

Expand Down
Loading