CORDA-696: Contract upgrade transactions - handle WhitelistedByZoneAttachmentConstraint for input state constraint verification

This commit is contained in:
Andrius Dagys 2018-02-27 17:27:47 +00:00
parent 067c9fb75f
commit 37de2e770e

View File

@ -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, *>