mirror of
https://github.com/corda/corda.git
synced 2025-01-19 11:16:54 +00:00
CORDA-696: Contract upgrade transactions - handle WhitelistedByZoneAttachmentConstraint for input state constraint verification
This commit is contained in:
parent
067c9fb75f
commit
37de2e770e
@ -48,7 +48,6 @@ data class ContractUpgradeWireTransaction(
|
|||||||
/** Resolves input states and contract attachments, and builds a ContractUpgradeLedgerTransaction. */
|
/** Resolves input states and contract attachments, and builds a ContractUpgradeLedgerTransaction. */
|
||||||
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
fun resolve(services: ServicesForResolution, sigs: List<TransactionSignature>): ContractUpgradeLedgerTransaction {
|
||||||
val resolvedInputs = services.loadStates(inputs.toSet()).toList()
|
val resolvedInputs = services.loadStates(inputs.toSet()).toList()
|
||||||
val legacyContractClassName = resolvedInputs.first().state.contract
|
|
||||||
val legacyContractAttachment = services.attachments.openAttachment(legacyContractAttachmentId)
|
val legacyContractAttachment = services.attachments.openAttachment(legacyContractAttachmentId)
|
||||||
?: throw AttachmentResolutionException(legacyContractAttachmentId)
|
?: throw AttachmentResolutionException(legacyContractAttachmentId)
|
||||||
val upgradedContractAttachment = services.attachments.openAttachment(upgradedContractAttachmentId)
|
val upgradedContractAttachment = services.attachments.openAttachment(upgradedContractAttachmentId)
|
||||||
@ -56,8 +55,9 @@ data class ContractUpgradeWireTransaction(
|
|||||||
return ContractUpgradeLedgerTransaction(
|
return ContractUpgradeLedgerTransaction(
|
||||||
resolvedInputs,
|
resolvedInputs,
|
||||||
notary,
|
notary,
|
||||||
ContractAttachment(legacyContractAttachment, legacyContractClassName),
|
legacyContractAttachment,
|
||||||
ContractAttachment(upgradedContractAttachment, upgradeContractClassName),
|
upgradeContractClassName,
|
||||||
|
upgradedContractAttachment,
|
||||||
id,
|
id,
|
||||||
privacySalt,
|
privacySalt,
|
||||||
sigs,
|
sigs,
|
||||||
@ -102,40 +102,47 @@ data class ContractUpgradeFilteredTransaction(
|
|||||||
data class ContractUpgradeLedgerTransaction(
|
data class ContractUpgradeLedgerTransaction(
|
||||||
override val inputs: List<StateAndRef<ContractState>>,
|
override val inputs: List<StateAndRef<ContractState>>,
|
||||||
override val notary: Party,
|
override val notary: Party,
|
||||||
val legacyContractAttachment: ContractAttachment,
|
val legacyContractAttachment: Attachment,
|
||||||
val upgradedContractAttachment: ContractAttachment,
|
val upgradeContractClassName: ContractClassName,
|
||||||
|
val upgradedContractAttachment: Attachment,
|
||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
val privacySalt: PrivacySalt,
|
val privacySalt: PrivacySalt,
|
||||||
override val sigs: List<TransactionSignature>,
|
override val sigs: List<TransactionSignature>,
|
||||||
private val networkParameters: NetworkParameters
|
private val networkParameters: NetworkParameters
|
||||||
) : FullTransaction(), TransactionWithSignatures {
|
) : FullTransaction(), TransactionWithSignatures {
|
||||||
|
/** The legacy contract class name is determined by the first input state. */
|
||||||
|
private val legacyContractClassName = inputs.first().state.contract
|
||||||
private val upgradedContract: UpgradedContract<ContractState, *> = loadUpgradedContract()
|
private val upgradedContract: UpgradedContract<ContractState, *> = loadUpgradedContract()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// TODO: relax this constraint once upgrading encumbered states is supported
|
// TODO: relax this constraint once upgrading encumbered states is supported.
|
||||||
check(inputs.all { it.state.contract == legacyContractAttachment.contract }) {
|
check(inputs.all { it.state.contract == legacyContractClassName }) {
|
||||||
"All input states must point to the legacy contract"
|
"All input states must point to the legacy contract"
|
||||||
}
|
}
|
||||||
check(inputs.all { it.state.constraint.isSatisfiedBy(legacyContractAttachment) }) {
|
check(upgradedContract.legacyContract == legacyContractClassName) {
|
||||||
"Legacy contract constraint does not satisfy the constraint of the input states"
|
|
||||||
}
|
|
||||||
verifyLegacyContractConstraint()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun verifyLegacyContractConstraint() {
|
|
||||||
check(upgradedContract.legacyContract == legacyContractAttachment.contract) {
|
|
||||||
"Outputs' contract must be an upgraded version of the inputs' contract"
|
"Outputs' contract must be an upgraded version of the inputs' contract"
|
||||||
}
|
}
|
||||||
val attachmentWithContext = AttachmentWithContext(
|
verifyConstraints()
|
||||||
legacyContractAttachment,
|
}
|
||||||
|
|
||||||
|
private fun verifyConstraints() {
|
||||||
|
val attachmentForConstraintVerification = AttachmentWithContext(
|
||||||
|
legacyContractAttachment as? ContractAttachment
|
||||||
|
?: ContractAttachment(legacyContractAttachment, legacyContractClassName),
|
||||||
upgradedContract.legacyContract,
|
upgradedContract.legacyContract,
|
||||||
networkParameters.whitelistedContractImplementations
|
networkParameters.whitelistedContractImplementations
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: exclude encumbrance states from this check
|
||||||
|
check(inputs.all { it.state.constraint.isSatisfiedBy(attachmentForConstraintVerification) }) {
|
||||||
|
"Legacy contract constraint does not satisfy the constraint of the input states"
|
||||||
|
}
|
||||||
|
|
||||||
val constraintCheck = if (upgradedContract is UpgradedContractWithLegacyConstraint) {
|
val constraintCheck = if (upgradedContract is UpgradedContractWithLegacyConstraint) {
|
||||||
upgradedContract.legacyContractConstraint.isSatisfiedBy(attachmentWithContext)
|
upgradedContract.legacyContractConstraint.isSatisfiedBy(attachmentForConstraintVerification)
|
||||||
} else {
|
} else {
|
||||||
// If legacy constraint not specified, defaulting to WhitelistedByZoneAttachmentConstraint
|
// If legacy constraint not specified, defaulting to WhitelistedByZoneAttachmentConstraint
|
||||||
WhitelistedByZoneAttachmentConstraint.isSatisfiedBy(attachmentWithContext)
|
WhitelistedByZoneAttachmentConstraint.isSatisfiedBy(attachmentForConstraintVerification)
|
||||||
}
|
}
|
||||||
check(constraintCheck) {
|
check(constraintCheck) {
|
||||||
"Legacy contract does not satisfy the upgraded contract's constraint"
|
"Legacy contract does not satisfy the upgraded contract's constraint"
|
||||||
@ -158,7 +165,7 @@ data class ContractUpgradeLedgerTransaction(
|
|||||||
// TODO: re-map encumbrance pointers
|
// TODO: re-map encumbrance pointers
|
||||||
input.state.copy(
|
input.state.copy(
|
||||||
data = upgradedState,
|
data = upgradedState,
|
||||||
contract = upgradedContractAttachment.contract,
|
contract = upgradeContractClassName,
|
||||||
constraint = outputConstraint
|
constraint = outputConstraint
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -175,7 +182,7 @@ data class ContractUpgradeLedgerTransaction(
|
|||||||
private fun loadUpgradedContract(): UpgradedContract<ContractState, *> {
|
private fun loadUpgradedContract(): UpgradedContract<ContractState, *> {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return this::class.java.classLoader
|
return this::class.java.classLoader
|
||||||
.loadClass(upgradedContractAttachment.contract)
|
.loadClass(upgradeContractClassName)
|
||||||
.asSubclass(Contract::class.java)
|
.asSubclass(Contract::class.java)
|
||||||
.getConstructor()
|
.getConstructor()
|
||||||
.newInstance() as UpgradedContract<ContractState, *>
|
.newInstance() as UpgradedContract<ContractState, *>
|
||||||
|
Loading…
Reference in New Issue
Block a user