mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-19 08:48:21 +03:00
Adapt soft-upgrade-integration-tests to match latest required behaviour. (#17083)
This commit is contained in:
parent
e5ec18a581
commit
15052dd89f
@ -115,22 +115,24 @@ private[lf] object Compiler {
|
||||
def cast: SBuiltin
|
||||
}
|
||||
object Casting {
|
||||
final case class Hard(tmplId: TypeConName) extends Casting {
|
||||
final case class Hard(
|
||||
tmplId: TypeConName,
|
||||
fields: ImmArray[(FieldName, Type)],
|
||||
) extends Casting {
|
||||
def cast: SBuiltin = {
|
||||
SBCastAnyContract(tmplId)
|
||||
SBCastAnyContract(tmplId, fields, allowUpgradesAndDowngrades = false)
|
||||
}
|
||||
}
|
||||
final case class Soft(
|
||||
tmplId: TypeConName,
|
||||
fields: ImmArray[(FieldName, Type)],
|
||||
// TODO: https://github.com/digital-asset/daml/issues/17082
|
||||
// - predPids is ignored for milestone-1
|
||||
// - might be useful in following milestones
|
||||
predPids: List[PackageId],
|
||||
) extends Casting {
|
||||
def cast: SBuiltin = {
|
||||
SBPromoteAnyContract(
|
||||
tmplId,
|
||||
fields,
|
||||
predPids.map(Identifier(_, tmplId.qualifiedName)),
|
||||
)
|
||||
SBCastAnyContract(tmplId, fields, allowUpgradesAndDowngrades = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,7 +403,7 @@ private[lf] final class Compiler(
|
||||
}
|
||||
|
||||
addDef(compileCreate(tmplId, tmpl))
|
||||
addDef(compileFetchTemplate(tmplId, tmpl))
|
||||
addDef(compileFetchTemplate(tmplId, tmpl, fields))
|
||||
addDef(compileSoftFetchTemplate(tmplId, tmpl, fields, predPids))
|
||||
addDef(compileTemplatePreCondition(tmplId, tmpl))
|
||||
addDef(compileAgreementText(tmplId, tmpl))
|
||||
@ -419,7 +421,7 @@ private[lf] final class Compiler(
|
||||
}
|
||||
|
||||
tmpl.choices.values.foreach { choice =>
|
||||
addDef(compileTemplateChoice(tmplId, tmpl, choice))
|
||||
addDef(compileTemplateChoice(tmplId, tmpl, fields, choice))
|
||||
addDef(compileSoftTemplateChoice(tmplId, tmpl, fields, predPids, choice))
|
||||
addDef(compileChoiceController(tmplId, tmpl.param, choice))
|
||||
addDef(compileChoiceObserver(tmplId, tmpl.param, choice))
|
||||
@ -427,10 +429,10 @@ private[lf] final class Compiler(
|
||||
|
||||
tmpl.key.foreach { tmplKey =>
|
||||
addDef(compileContractKeyWithMaintainers(tmplId, tmpl, tmplKey))
|
||||
addDef(compileFetchByKey(tmplId, tmpl, tmplKey))
|
||||
addDef(compileFetchByKey(tmplId, tmpl, fields, tmplKey))
|
||||
addDef(compileLookupByKey(tmplId, tmplKey))
|
||||
tmpl.choices.values.foreach { x =>
|
||||
addDef(compileChoiceByKey(tmplId, tmpl, tmplKey, x))
|
||||
addDef(compileChoiceByKey(tmplId, tmpl, fields, tmplKey, x))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -651,6 +653,7 @@ private[lf] final class Compiler(
|
||||
private[this] def compileTemplateChoice(
|
||||
tmplId: TypeConName,
|
||||
tmpl: Template,
|
||||
fields: ImmArray[(FieldName, Type)],
|
||||
choice: TemplateChoice,
|
||||
): (t.SDefinitionRef, SDefinition) =
|
||||
// Compiles a choice into:
|
||||
@ -662,7 +665,7 @@ private[lf] final class Compiler(
|
||||
// in <retValue>
|
||||
topLevelFunction3(t.TemplateChoiceDefRef(tmplId, choice.name)) {
|
||||
(cidPos, choiceArgPos, tokenPos, env) =>
|
||||
val casting = Casting.Hard(tmplId)
|
||||
val casting = Casting.Hard(tmplId, fields)
|
||||
translateChoiceBody(env, tmplId, casting, tmpl, choice)(
|
||||
choiceArgPos,
|
||||
cidPos,
|
||||
@ -731,6 +734,7 @@ private[lf] final class Compiler(
|
||||
private[this] def compileChoiceByKey(
|
||||
tmplId: TypeConName,
|
||||
tmpl: Template,
|
||||
fields: ImmArray[(FieldName, Type)],
|
||||
tmplKey: TemplateKey,
|
||||
choice: TemplateChoice,
|
||||
): (t.SDefinitionRef, SDefinition) =
|
||||
@ -747,7 +751,7 @@ private[lf] final class Compiler(
|
||||
(keyPos, choiceArgPos, tokenPos, env) =>
|
||||
let(env, translateKeyWithMaintainers(env, keyPos, tmplKey)) { (keyWithMPos, env) =>
|
||||
let(env, SBUFetchKey(tmplId)(env.toSEVar(keyWithMPos))) { (cidPos, env) =>
|
||||
val casting = Casting.Hard(tmplId)
|
||||
val casting = Casting.Hard(tmplId, fields)
|
||||
translateChoiceBody(env, tmplId, casting, tmpl, choice)(
|
||||
choiceArgPos,
|
||||
cidPos,
|
||||
@ -791,6 +795,7 @@ private[lf] final class Compiler(
|
||||
private[this] def compileFetchTemplate(
|
||||
tmplId: Identifier,
|
||||
tmpl: Template,
|
||||
fields: ImmArray[(FieldName, Type)],
|
||||
): (t.SDefinitionRef, SDefinition) =
|
||||
// compile a template to
|
||||
// FetchDefRef(tmplId) = \ <coid> <token> ->
|
||||
@ -798,7 +803,7 @@ private[lf] final class Compiler(
|
||||
// _ = $insertFetch(tmplId, false) coid [tmpl.signatories] [tmpl.observers] [tmpl.key]
|
||||
// in <tmplArg>
|
||||
topLevelFunction2(t.FetchTemplateDefRef(tmplId)) { (cidPos, tokenPos, env) =>
|
||||
val casting = Casting.Hard(tmplId)
|
||||
val casting = Casting.Hard(tmplId, fields)
|
||||
translateFetchTemplateBody(env, tmplId, tmpl, casting)(cidPos, None, tokenPos)
|
||||
}
|
||||
|
||||
@ -1051,6 +1056,7 @@ private[lf] final class Compiler(
|
||||
private[this] def compileFetchByKey(
|
||||
tmplId: TypeConName,
|
||||
tmpl: Template,
|
||||
fields: ImmArray[(FieldName, Type)],
|
||||
tmplKey: TemplateKey,
|
||||
): (t.SDefinitionRef, SDefinition) =
|
||||
// compile a template with key into
|
||||
@ -1065,7 +1071,7 @@ private[lf] final class Compiler(
|
||||
let(env, SBUFetchKey(tmplId)(env.toSEVar(keyWithMPos))) { (cidPos, env) =>
|
||||
let(
|
||||
env,
|
||||
translateFetchTemplateBody(env, tmplId, tmpl, Casting.Hard(tmplId))(
|
||||
translateFetchTemplateBody(env, tmplId, tmpl, Casting.Hard(tmplId, fields))(
|
||||
cidPos,
|
||||
Some(keyWithMPos),
|
||||
tokenPos,
|
||||
|
@ -10,7 +10,6 @@ import com.daml.lf.data._
|
||||
import com.daml.lf.data.Numeric.Scale
|
||||
import com.daml.lf.interpretation.{Error => IE}
|
||||
import com.daml.lf.language.Ast
|
||||
import com.daml.lf.language.Util.TOptional
|
||||
import com.daml.lf.speedy.ArrayList.Implicits._
|
||||
import com.daml.lf.speedy.SError._
|
||||
import com.daml.lf.speedy.SExpr._
|
||||
@ -1075,88 +1074,63 @@ private[lf] object SBuiltin {
|
||||
}
|
||||
|
||||
// SBCastAnyContract: ContractId templateId -> Any -> templateId
|
||||
final case class SBCastAnyContract(templateId: TypeConName) extends SBuiltin(2) {
|
||||
override private[speedy] def execute[Q](
|
||||
args: util.ArrayList[SValue],
|
||||
machine: Machine[Q],
|
||||
): Control[Nothing] = {
|
||||
def coid = getSContractId(args, 0)
|
||||
val (actualTemplateId, record) = getSAnyContract(args, 1)
|
||||
if (actualTemplateId != templateId) {
|
||||
Control.Error(IE.WronglyTypedContract(coid, templateId, actualTemplateId))
|
||||
} else {
|
||||
Control.Value(record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SBPromoteAnyContract(templateId): ContractId actualTemplateId -> Any -> templateId
|
||||
// Fails unless actualTemplateId is a predecessor of templateId.
|
||||
final case class SBPromoteAnyContract(
|
||||
final case class SBCastAnyContract(
|
||||
templateId: TypeConName,
|
||||
fields: ImmArray[(Ast.FieldName, Ast.Type)],
|
||||
acceptedTemplateIds: List[TypeConName],
|
||||
targetFieldsAndTypes: ImmArray[(Ast.FieldName, Ast.Type)],
|
||||
// TODO: https://github.com/digital-asset/daml/issues/17082
|
||||
// - allowUpgradesAndDowngrades will come from a feature flag
|
||||
allowUpgradesAndDowngrades: Boolean,
|
||||
) extends SBuiltin(2) {
|
||||
|
||||
// TODO: https://github.com/digital-asset/daml/issues/17082
|
||||
// - we currently make no use of the types
|
||||
val targetFields = targetFieldsAndTypes.map(_._1)
|
||||
|
||||
override private[speedy] def execute[Q](
|
||||
args: util.ArrayList[SValue],
|
||||
machine: Machine[Q],
|
||||
): Control[Nothing] = {
|
||||
|
||||
// TODO https://github.com/digital-asset/daml/issues/16151
|
||||
// debug calls to SBPromoteAnyContract
|
||||
// println(s"**SBPromoteAnyContract: \n- templateId=${templateId}\n- acceptedTemplateIds=${acceptedTemplateIds}");
|
||||
|
||||
// TODO https://github.com/digital-asset/daml/issues/16151
|
||||
// perhaps compute `acceptedTemplateIds` here, dynamically when execute is called
|
||||
// And then be can be more confident that all relavant package have been compiled.
|
||||
|
||||
def coid = getSContractId(args, 0)
|
||||
val (actualTemplateId, record) = getSAnyContract(args, 1)
|
||||
|
||||
if ((templateId +: acceptedTemplateIds).contains(actualTemplateId)) {
|
||||
// Here we extend values of predecessor template types by adding the
|
||||
// right number of 'None's for missing 'Optional' fields
|
||||
// TODO: https://github.com/digital-asset/daml/issues/16151
|
||||
// For the PoC, this assumes field order is preserved by later versions
|
||||
// and that new fields are only added at the end.
|
||||
|
||||
// TODO: https://github.com/digital-asset/daml/issues/16151
|
||||
// This `SBPromoteAnyContract' code will be reworked shortly.
|
||||
// add-Node/drop-None will be moved to importValue
|
||||
|
||||
val newFields = fields.iterator.drop(record.values.size)
|
||||
val badNewFields = newFields.filter {
|
||||
case (name @ _, TOptional(_)) => false
|
||||
case _ => true
|
||||
}
|
||||
if (badNewFields.isEmpty) {
|
||||
Control.Value(
|
||||
if (actualTemplateId == templateId) {
|
||||
// No upgrade or downgrade required
|
||||
Control.Value(record)
|
||||
} else {
|
||||
assert(actualTemplateId != templateId)
|
||||
if (!allowUpgradesAndDowngrades) {
|
||||
// Existing behavior retained when the up/down-grades feature is disabled.
|
||||
val coid = getSContractId(args, 0)
|
||||
Control.Error(IE.WronglyTypedContract(coid, templateId, actualTemplateId))
|
||||
} else {
|
||||
assert(allowUpgradesAndDowngrades)
|
||||
// TODO: https://github.com/digital-asset/daml/issues/17082
|
||||
// This code which implements the compatibility relation (i.e drop/make-None) must
|
||||
// move to be within importValue/translateValue
|
||||
val actualValues = record.values
|
||||
val numActual: Int = record.fields.length
|
||||
val numTarget: Int = targetFields.length
|
||||
val patchedValues =
|
||||
if (numTarget > numActual) {
|
||||
// Upgrade -- extend with None
|
||||
actualValues.asScala.padTo(targetFields.length, SOptional(None)).to(ArrayList)
|
||||
} else if (numTarget < numActual) {
|
||||
// Downgrade -- truncate
|
||||
// TODO: https://github.com/digital-asset/daml/issues/17082
|
||||
// - disallow dropping extra fields which have value other than None.
|
||||
actualValues.asScala.take(targetFields.length).to(ArrayList)
|
||||
} else {
|
||||
// Correct number of fields.
|
||||
assert(numTarget == numActual)
|
||||
actualValues // do nothing
|
||||
}
|
||||
val patched: SRecord = {
|
||||
SRecord(
|
||||
id = templateId,
|
||||
fields = fields.map(_._1),
|
||||
values = record.values.asScala.padTo(fields.length, SOptional(None)).to(ArrayList),
|
||||
fields = targetFields,
|
||||
values = patchedValues,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// For the PoC, it is fine to crash here since we assume all new
|
||||
// fields will be of type Optional(_). After the PoC, this will
|
||||
// be checked at daml-compile and package-upload times.
|
||||
crash(
|
||||
s"SBPromoteAnyContract[bad new fields]:\n" +
|
||||
s" contractId = ${coid}" +
|
||||
s" expectedTemplateId = ${templateId}" +
|
||||
s" acceptedTemplateIds = ${acceptedTemplateIds}" +
|
||||
s" actualTemplateId = ${actualTemplateId}" +
|
||||
s" badNewFields = ${badNewFields}"
|
||||
)
|
||||
}
|
||||
Control.Value(patched)
|
||||
}
|
||||
} else {
|
||||
Control.Error(
|
||||
IE.Dev(
|
||||
NameOf.qualifiedNameOfCurrentFunc,
|
||||
IE.Dev.WronglyTypedContractSoft(coid, templateId, acceptedTemplateIds, actualTemplateId),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,8 +187,13 @@ object SValue {
|
||||
|
||||
object SRecord {
|
||||
def apply(id: Identifier, fields: ImmArray[Name], values: util.ArrayList[SValue]): SRecord = {
|
||||
assert(fields.length == values.size)
|
||||
val zipped = (fields.toSeq zip values.asScala).toList.reverse
|
||||
if (fields.length != values.size) {
|
||||
throw SError.SErrorCrash(
|
||||
NameOf.qualifiedNameOfCurrentFunc,
|
||||
s"SRecord.apply(#fields=${fields.length}; #values=${values.size}: mismatch!\n- fields=${fields}\n- values=${values}",
|
||||
)
|
||||
}
|
||||
val zipped = (fields.toSeq zip values.asScala).toList
|
||||
SRecordRep(id, fields, zipped.toMap)
|
||||
}
|
||||
def unapply(x: SRecord): Option[(Identifier, ImmArray[Name], util.ArrayList[SValue])] = {
|
||||
|
@ -103,7 +103,7 @@ final class DevIT extends AsyncWordSpec with AbstractScriptTest with Inside with
|
||||
} yield r shouldBe SUnit
|
||||
}
|
||||
|
||||
"succeed when given a contract id of a predecessor type of Coin V2 (with a new field), Coin V1" in {
|
||||
"succeed when given a contract id of a predecessor type of Coin V2 (with a new field), Coin V1 -- Upgrade" in {
|
||||
for {
|
||||
clients <- scriptClients()
|
||||
r <-
|
||||
@ -115,7 +115,7 @@ final class DevIT extends AsyncWordSpec with AbstractScriptTest with Inside with
|
||||
} yield r shouldBe SUnit
|
||||
}
|
||||
|
||||
"fail when given a contract id of a non-predecessor type of Coin V1, Coin V2" in {
|
||||
"succeed when given a contract id of a non-predecessor type of Coin V1, Coin V2 -- Downgrade" in {
|
||||
for {
|
||||
clients <- scriptClients()
|
||||
r <-
|
||||
@ -127,7 +127,7 @@ final class DevIT extends AsyncWordSpec with AbstractScriptTest with Inside with
|
||||
} yield r shouldBe SUnit
|
||||
}
|
||||
|
||||
"fail when given a contract id of a non-predecessor type of Coin V1, Coin V2 (with new field = None)" in {
|
||||
"succeed when given a contract id of a non-predecessor type of Coin V1, Coin V2 (with new field = None) -- Downgrade/drop-None" in {
|
||||
for {
|
||||
clients <- scriptClients()
|
||||
r <-
|
||||
@ -139,7 +139,9 @@ final class DevIT extends AsyncWordSpec with AbstractScriptTest with Inside with
|
||||
} yield r shouldBe SUnit
|
||||
}
|
||||
|
||||
"fail when given a contract id of a non-predecessor type of Coin V1, Coin V2 (with new field = Some _)" in {
|
||||
// TODO: https://github.com/digital-asset/daml/issues/17082
|
||||
// enable this test when it works!
|
||||
"fail when given a contract id of a non-predecessor type of Coin V1, Coin V2 (with new field = Some _) -- refuse Downgrade/drop-Some" ignore {
|
||||
for {
|
||||
clients <- scriptClients()
|
||||
r <-
|
||||
@ -163,7 +165,7 @@ final class DevIT extends AsyncWordSpec with AbstractScriptTest with Inside with
|
||||
} yield r shouldBe SUnit
|
||||
}
|
||||
|
||||
"fail when given a contract id of a non-predecessor type of Coin V1, Coin V3" in {
|
||||
"succeed when given a contract id of a non-predecessor type of Coin V1, Coin V3 -- Downgrade" in {
|
||||
for {
|
||||
clients <- scriptClients()
|
||||
r <-
|
||||
|
@ -58,7 +58,7 @@ create_v2_none_softFetch_v1 = do
|
||||
owner = alice
|
||||
obs = []
|
||||
ccy = None
|
||||
_ <- alice `submitMustFail` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1 with
|
||||
_ <- alice `submit` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1 with -- Downgrade/drop-None
|
||||
cid = coerceContractId cid
|
||||
pure ()
|
||||
|
||||
@ -70,6 +70,6 @@ create_v2_some_softFetch_v1 = do
|
||||
owner = alice
|
||||
obs = []
|
||||
ccy = Some "CHF"
|
||||
_ <- alice `submitMustFail` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1 with
|
||||
_ <- alice `submitMustFail` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1 with -- refuse Downgrade/drop-Some
|
||||
cid = coerceContractId cid
|
||||
pure ()
|
||||
|
@ -62,7 +62,7 @@ create_v1_softFetch_v2 = do
|
||||
issuer = alice
|
||||
owner = alice
|
||||
obs = []
|
||||
_ <- alice `submit` createAndExerciseCmd (Aux alice) SoftFetch_Coin_2_0_0 with
|
||||
_ <- alice `submit` createAndExerciseCmd (Aux alice) SoftFetch_Coin_2_0_0 with -- Upgrade
|
||||
cid = coerceContractId cid
|
||||
pure ()
|
||||
|
||||
@ -73,7 +73,7 @@ create_v2_softFetch_v1 = do
|
||||
issuer = alice
|
||||
owner = alice
|
||||
obs = []
|
||||
_ <- alice `submitMustFail` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1_0_0 with
|
||||
_ <- alice `submit` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1_0_0 with -- Downgrade
|
||||
cid = coerceContractId cid
|
||||
pure ()
|
||||
|
||||
@ -125,6 +125,6 @@ create_v1_softExercise_v2 = do
|
||||
issuer = alice
|
||||
owner = alice
|
||||
obs = [bob]
|
||||
_ <- bob `submit` createAndExerciseCmd (Aux2 bob) SoftExercise_Steal_1_0_0 with
|
||||
_ <- bob `submit` createAndExerciseCmd (Aux2 bob) SoftExercise_Steal_1_0_0 with -- Upgrade
|
||||
cid = cid
|
||||
pure ()
|
||||
|
@ -40,7 +40,7 @@ create_v1_softFetch_v3 = do
|
||||
issuer = alice
|
||||
owner = alice
|
||||
obs = []
|
||||
_ <- alice `submit` createAndExerciseCmd (Aux alice) SoftFetch_Coin_3_0_0 with
|
||||
_ <- alice `submit` createAndExerciseCmd (Aux alice) SoftFetch_Coin_3_0_0 with -- Upgrade
|
||||
cid = coerceContractId cid
|
||||
pure ()
|
||||
|
||||
@ -51,6 +51,6 @@ create_v3_softFetch_v1 = do
|
||||
issuer = alice
|
||||
owner = alice
|
||||
obs = []
|
||||
_ <- alice `submitMustFail` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1_0_0 with
|
||||
_ <- alice `submit` createAndExerciseCmd (Aux alice) SoftFetch_Coin_1_0_0 with -- Downgrade
|
||||
cid = coerceContractId cid
|
||||
pure ()
|
||||
|
Loading…
Reference in New Issue
Block a user