Replaces keys and parties in states with AbstractParty

Switch to using AbstractParty as the standard identifier for parties in
states, so that full parties can be used during construction of
transactions and anonymised parties when the transaction is being added
to the ledger.
This commit is contained in:
Ross Nicoll
2017-05-11 11:25:00 +01:00
parent d3bb040355
commit c13a99a2f9
110 changed files with 786 additions and 720 deletions

View File

@ -10,6 +10,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.BusinessCalendar import net.corda.core.contracts.BusinessCalendar
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
@ -66,6 +67,7 @@ object JacksonSupport {
addDeserializer(AnonymousParty::class.java, AnonymousPartyDeserializer) addDeserializer(AnonymousParty::class.java, AnonymousPartyDeserializer)
addSerializer(Party::class.java, PartySerializer) addSerializer(Party::class.java, PartySerializer)
addDeserializer(Party::class.java, PartyDeserializer) addDeserializer(Party::class.java, PartyDeserializer)
addDeserializer(AbstractParty::class.java, PartyDeserializer)
addSerializer(BigDecimal::class.java, ToStringSerializer) addSerializer(BigDecimal::class.java, ToStringSerializer)
addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer()) addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer())
addSerializer(SecureHash::class.java, SecureHashSerializer) addSerializer(SecureHash::class.java, SecureHashSerializer)
@ -160,8 +162,16 @@ object JacksonSupport {
} }
val mapper = parser.codec as PartyObjectMapper val mapper = parser.codec as PartyObjectMapper
// TODO: We should probably have a better specified way of identifying X.500 names vs keys
// Base58 keys never include an equals character, while X.500 names always will, so we use that to determine
// how to parse the content
return if (parser.text.contains("=")) {
val principal = X500Name(parser.text) val principal = X500Name(parser.text)
return mapper.partyFromPrincipal(principal) ?: throw JsonParseException(parser, "Could not find a Party with name ${principal}") mapper.partyFromPrincipal(principal) ?: throw JsonParseException(parser, "Could not find a Party with name ${principal}")
} else {
val key = parsePublicKeyBase58(parser.text)
mapper.partyFromKey(key) ?: throw JsonParseException(parser, "Could not find a Party with key ${key.toStringShort()}")
}
} }
} }

View File

@ -115,7 +115,7 @@ inline fun <reified T : MoveCommand> verifyMoveCommand(inputs: List<OwnableState
// Now check the digital signatures on the move command. Every input has an owning public key, and we must // Now check the digital signatures on the move command. Every input has an owning public key, and we must
// see a signature from each of those keys. The actual signatures have been verified against the transaction // see a signature from each of those keys. The actual signatures have been verified against the transaction
// data by the platform before execution. // data by the platform before execution.
val owningPubKeys = inputs.map { it.owner }.toSet() val owningPubKeys = inputs.map { it.owner.owningKey }.toSet()
val command = commands.requireSingleCommand<T>() val command = commands.requireSingleCommand<T>()
val keysThatSigned = command.signers.toSet() val keysThatSigned = command.signers.toSet()
requireThat { requireThat {

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
@ -14,12 +14,12 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
val magicNumber: Int val magicNumber: Int
} }
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKey) : OwnableState, State { data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: AbstractParty) : OwnableState, State {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
} }
/** /**
@ -28,9 +28,9 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
* in a different field, however this is a good example of a contract with multiple states. * in a different field, however this is a good example of a contract with multiple states.
*/ */
data class MultiOwnerState(override val magicNumber: Int = 0, data class MultiOwnerState(override val magicNumber: Int = 0,
val owners: List<PublicKey>) : ContractState, State { val owners: List<AbstractParty>) : ContractState, State {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey> get() = owners override val participants: List<AbstractParty> get() = owners
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -47,22 +47,22 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
fun generateInitial(magicNumber: Int, notary: Party, owner: PartyAndReference, vararg otherOwners: PartyAndReference): TransactionBuilder { fun generateInitial(magicNumber: Int, notary: Party, owner: PartyAndReference, vararg otherOwners: PartyAndReference): TransactionBuilder {
val owners = listOf(owner) + otherOwners val owners = listOf(owner) + otherOwners
return if (owners.size == 1) { return if (owners.size == 1) {
val state = SingleOwnerState(magicNumber, owners.first().party.owningKey) val state = SingleOwnerState(magicNumber, owners.first().party)
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.first().party.owningKey)) TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.first().party.owningKey))
} else { } else {
val state = MultiOwnerState(magicNumber, owners.map { it.party.owningKey }) val state = MultiOwnerState(magicNumber, owners.map { it.party })
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.map { it.party.owningKey })) TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.map { it.party.owningKey }))
} }
} }
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner) fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: AbstractParty) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder { fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: AbstractParty): TransactionBuilder {
require(priors.isNotEmpty()) require(priors.isNotEmpty())
val priorState = priors[0].state.data val priorState = priors[0].state.data
val (cmd, state) = priorState.withNewOwner(newOwner) val (cmd, state) = priorState.withNewOwner(newOwner)
return TransactionType.General.Builder(notary = priors[0].state.notary).withItems( return TransactionType.General.Builder(notary = priors[0].state.notary).withItems(
/* INPUTS */ *priors.toTypedArray(), /* INPUTS */ *priors.toTypedArray(),
/* COMMAND */ Command(cmd, priorState.owner), /* COMMAND */ Command(cmd, priorState.owner.owningKey),
/* OUTPUT */ state /* OUTPUT */ state
) )
} }

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.flows.ContractUpgradeFlow import net.corda.flows.ContractUpgradeFlow
import java.security.PublicKey
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
val DUMMY_V2_PROGRAM_ID = DummyContractV2() val DUMMY_V2_PROGRAM_ID = DummyContractV2()
@ -15,9 +15,9 @@ val DUMMY_V2_PROGRAM_ID = DummyContractV2()
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> { class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
override val legacyContract = DummyContract::class.java override val legacyContract = DummyContract::class.java
data class State(val magicNumber: Int = 0, val owners: List<PublicKey>) : ContractState { data class State(val magicNumber: Int = 0, val owners: List<AbstractParty>) : ContractState {
override val contract = DUMMY_V2_PROGRAM_ID override val contract = DUMMY_V2_PROGRAM_ID
override val participants: List<PublicKey> = owners override val participants: List<AbstractParty> = owners
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -44,16 +44,16 @@ class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.St
* *
* @return a pair of wire transaction, and a set of those who should sign the transaction for it to be valid. * @return a pair of wire transaction, and a set of those who should sign the transaction for it to be valid.
*/ */
fun generateUpgradeFromV1(vararg states: StateAndRef<DummyContract.State>): Pair<WireTransaction, Set<PublicKey>> { fun generateUpgradeFromV1(vararg states: StateAndRef<DummyContract.State>): Pair<WireTransaction, Set<AbstractParty>> {
val notary = states.map { it.state.notary }.single() val notary = states.map { it.state.notary }.single()
require(states.isNotEmpty()) require(states.isNotEmpty())
val signees = states.flatMap { it.state.data.participants }.toSet() val signees: Set<AbstractParty> = states.flatMap { it.state.data.participants }.distinct().toSet()
return Pair(TransactionType.General.Builder(notary).apply { return Pair(TransactionType.General.Builder(notary).apply {
states.forEach { states.forEach {
addInputState(it) addInputState(it)
addOutputState(upgrade(it.state.data)) addOutputState(upgrade(it.state.data))
addCommand(UpgradeCommand(DUMMY_V2_PROGRAM_ID.javaClass), signees.toList()) addCommand(UpgradeCommand(DUMMY_V2_PROGRAM_ID.javaClass), signees.map { it.owningKey }.toList())
} }
}.toWireTransaction(), signees) }.toWireTransaction(), signees)
} }

View File

@ -1,12 +1,12 @@
package net.corda.core.contracts package net.corda.core.contracts
import java.security.PublicKey import net.corda.core.identity.AbstractParty
/** /**
* Dummy state for use in testing. Not part of any contract, not even the [DummyContract]. * Dummy state for use in testing. Not part of any contract, not even the [DummyContract].
*/ */
data class DummyState(val magicNumber: Int = 0) : ContractState { data class DummyState(val magicNumber: Int = 0) : ContractState {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = emptyList() get() = emptyList()
} }

View File

@ -1,12 +1,8 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.identity.Party
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.transactions.TransactionBuilder import net.corda.core.identity.AbstractParty
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import java.security.PublicKey import java.security.PublicKey
import java.util.*
class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing") class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing")
@ -32,9 +28,9 @@ interface FungibleAsset<T : Any> : OwnableState {
*/ */
val exitKeys: Collection<PublicKey> val exitKeys: Collection<PublicKey>
/** There must be a MoveCommand signed by this key to claim the amount */ /** There must be a MoveCommand signed by this key to claim the amount */
override val owner: PublicKey override val owner: AbstractParty
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKey): FungibleAsset<T> fun move(newAmount: Amount<Issued<T>>, newOwner: AbstractParty): FungibleAsset<T>
// Just for grouping // Just for grouping
interface Commands : CommandData { interface Commands : CommandData {

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.clauses.Clause
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogicRef import net.corda.core.flows.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
@ -114,7 +115,7 @@ interface ContractState {
* The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants * The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants
* list should just contain the owner. * list should just contain the owner.
*/ */
val participants: List<PublicKey> val participants: List<AbstractParty>
} }
/** /**
@ -174,10 +175,10 @@ fun <T : Any> Amount<Issued<T>>.withoutIssuer(): Amount<T> = Amount(quantity, to
*/ */
interface OwnableState : ContractState { interface OwnableState : ContractState {
/** There must be a MoveCommand signed by this key to claim the amount */ /** There must be a MoveCommand signed by this key to claim the amount */
val owner: PublicKey val owner: AbstractParty
/** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */ /** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */
fun withNewOwner(newOwner: PublicKey): Pair<CommandData, OwnableState> fun withNewOwner(newOwner: AbstractParty): Pair<CommandData, OwnableState>
} }
/** Something which is scheduled to happen at a point in time */ /** Something which is scheduled to happen at a point in time */
@ -280,7 +281,7 @@ interface DealState : LinearState {
* separate process exchange certificates to ascertain identities. Thus decoupling identities from * separate process exchange certificates to ascertain identities. Thus decoupling identities from
* [ContractState]s. * [ContractState]s.
* */ * */
val parties: List<AnonymousParty> val parties: List<AbstractParty>
/** /**
* Generate a partial transaction representing an agreement (command) to this deal, allowing a general * Generate a partial transaction representing an agreement (command) to this deal, allowing a general
@ -343,9 +344,7 @@ inline fun <reified T : ContractState> Iterable<StateAndRef<ContractState>>.filt
* ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party. * ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party.
*/ */
@CordaSerializable @CordaSerializable
data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) { data class PartyAndReference(val party: AbstractParty, val reference: OpaqueBytes) {
constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), reference)
override fun toString() = "$party$reference" override fun toString() = "$party$reference"
} }

View File

@ -148,7 +148,7 @@ sealed class TransactionType {
*/ */
class Builder(notary: Party) : TransactionBuilder(NotaryChange, notary) { class Builder(notary: Party) : TransactionBuilder(NotaryChange, notary) {
override fun addInputState(stateAndRef: StateAndRef<*>) { override fun addInputState(stateAndRef: StateAndRef<*>) {
signers.addAll(stateAndRef.state.data.participants) signers.addAll(stateAndRef.state.data.participants.map { it.owningKey })
super.addInputState(stateAndRef) super.addInputState(stateAndRef)
} }
} }
@ -171,6 +171,6 @@ sealed class TransactionType {
} }
} }
override fun getRequiredSigners(tx: LedgerTransaction) = tx.inputs.flatMap { it.state.data.participants }.toSet() override fun getRequiredSigners(tx: LedgerTransaction) = tx.inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
} }
} }

View File

@ -2,10 +2,11 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAPublicKey
import java.math.BigInteger import java.math.BigInteger
import java.security.* import java.security.*
@ -18,6 +19,8 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun toString() = "NULL_KEY" override fun toString() = "NULL_KEY"
} }
val NULL_PARTY = AnonymousParty(NullPublicKey)
// TODO: Clean up this duplication between Null and Dummy public key // TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable @CordaSerializable
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> { class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {

View File

@ -16,7 +16,6 @@ abstract class AbstractParty(val owningKey: PublicKey) {
override fun equals(other: Any?): Boolean = other is AbstractParty && this.owningKey == other.owningKey override fun equals(other: Any?): Boolean = other is AbstractParty && this.owningKey == other.owningKey
override fun hashCode(): Int = owningKey.hashCode() override fun hashCode(): Int = owningKey.hashCode()
abstract fun toAnonymous(): AnonymousParty
abstract fun nameOrNull(): X500Name? abstract fun nameOrNull(): X500Name?
abstract fun ref(bytes: OpaqueBytes): PartyAndReference abstract fun ref(bytes: OpaqueBytes): PartyAndReference

View File

@ -18,5 +18,4 @@ class AnonymousParty(owningKey: PublicKey) : AbstractParty(owningKey) {
override fun nameOrNull(): X500Name? = null override fun nameOrNull(): X500Name? = null
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes) override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
override fun toAnonymous() = this
} }

View File

@ -29,9 +29,8 @@ import java.security.PublicKey
// TODO: Remove "open" from [Party] once deprecated crypto.Party class is removed // TODO: Remove "open" from [Party] once deprecated crypto.Party class is removed
open class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) { open class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
constructor(certAndKey: CertificateAndKey) : this(X500Name(certAndKey.certificate.subjectDN.name), certAndKey.keyPair.public) constructor(certAndKey: CertificateAndKey) : this(X500Name(certAndKey.certificate.subjectDN.name), certAndKey.keyPair.public)
override fun toAnonymous(): AnonymousParty = AnonymousParty(owningKey)
override fun toString() = "${owningKey.toBase58String()} ($name)" override fun toString() = "${owningKey.toBase58String()} ($name)"
override fun nameOrNull(): X500Name? = name override fun nameOrNull(): X500Name? = name
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this.toAnonymous(), bytes) override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
} }

View File

@ -1,6 +1,7 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
@ -54,7 +55,7 @@ interface IdentityService {
fun partyFromName(name: String): Party? fun partyFromName(name: String): Party?
fun partyFromX500Name(principal: X500Name): Party? fun partyFromX500Name(principal: X500Name): Party?
fun partyFromAnonymous(party: AnonymousParty): Party? fun partyFromAnonymous(party: AbstractParty): Party?
fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
/** /**

View File

@ -3,7 +3,9 @@ package net.corda.core.node.services
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -286,7 +288,7 @@ interface VaultService {
@Suspendable @Suspendable
fun generateSpend(tx: TransactionBuilder, fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: PublicKey, to: AbstractParty,
onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<PublicKey>> onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<PublicKey>>
// DOCSTART VaultStatesQuery // DOCSTART VaultStatesQuery

View File

@ -7,6 +7,7 @@ import net.corda.core.contracts.StateRef
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -59,13 +60,13 @@ abstract class AbstractStateReplacementFlow {
progressTracker.currentStep = SIGNING progressTracker.currentStep = SIGNING
val myKey = serviceHub.myInfo.legalIdentity.owningKey val myKey = serviceHub.myInfo.legalIdentity
val me = listOf(myKey) val me = listOf(myKey)
val signatures = if (participants == me) { val signatures = if (participants == me) {
getNotarySignatures(stx) getNotarySignatures(stx)
} else { } else {
collectSignatures(participants - me, stx) collectSignatures((participants - me).map { it.owningKey }, stx)
} }
val finalTx = stx + signatures val finalTx = stx + signatures
@ -73,7 +74,7 @@ abstract class AbstractStateReplacementFlow {
return finalTx.tx.outRef(0) return finalTx.tx.outRef(0)
} }
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>> abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>>
@Suspendable @Suspendable
private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> { private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {

View File

@ -3,6 +3,7 @@ package net.corda.flows
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey import java.security.PublicKey
@ -32,12 +33,12 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
@JvmStatic @JvmStatic
fun verify(input: ContractState, output: ContractState, commandData: Command) { fun verify(input: ContractState, output: ContractState, commandData: Command) {
val command = commandData.value as UpgradeCommand val command = commandData.value as UpgradeCommand
val participants: Set<PublicKey> = input.participants.toSet() val participantKeys: Set<PublicKey> = input.participants.map { it.owningKey }.toSet()
val keysThatSigned: Set<PublicKey> = commandData.signers.toSet() val keysThatSigned: Set<PublicKey> = commandData.signers.toSet()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *> val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *>
requireThat { requireThat {
"The signing keys include all participant keys" using keysThatSigned.containsAll(participants) "The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys)
"Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract) "Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract)
"Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass) "Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass)
"Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input)) "Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input))
@ -53,11 +54,11 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
.withItems( .withItems(
stateRef, stateRef,
contractUpgrade.upgrade(stateRef.state.data), contractUpgrade.upgrade(stateRef.state.data),
Command(UpgradeCommand(upgradedContractClass), stateRef.state.data.participants)) Command(UpgradeCommand(upgradedContractClass), stateRef.state.data.participants.map { it.owningKey }))
} }
} }
override fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>> { override fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>> {
val stx = assembleBareTx(originalState, modification) val stx = assembleBareTx(originalState, modification)
.signWith(serviceHub.legalIdentityKey) .signWith(serviceHub.legalIdentityKey)
.toSignedTransaction(false) .toSignedTransaction(false)

View File

@ -105,7 +105,7 @@ class FinalityFlow(val transactions: Iterable<SignedTransaction>,
// Calculate who is meant to see the results based on the participants involved. // Calculate who is meant to see the results based on the participants involved.
val keys = ltx.outputs.flatMap { it.data.participants } + ltx.inputs.flatMap { it.state.data.participants } val keys = ltx.outputs.flatMap { it.data.participants } + ltx.inputs.flatMap { it.state.data.participants }
// TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them count as a reason to fail? // TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them count as a reason to fail?
val parties = keys.mapNotNull { serviceHub.identityService.partyFromKey(it) }.toSet() val parties = keys.mapNotNull { serviceHub.identityService.partyFromAnonymous(it) }.toSet()
Pair(stx, parties) Pair(stx, parties)
} }
} }

View File

@ -2,6 +2,7 @@ package net.corda.flows
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -24,11 +25,11 @@ class NotaryChangeFlow<out T : ContractState>(
progressTracker: ProgressTracker = tracker()) progressTracker: ProgressTracker = tracker())
: AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) { : AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) {
override fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>> { override fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>> {
val state = originalState.state val state = originalState.state
val tx = TransactionType.NotaryChange.Builder(originalState.state.notary) val tx = TransactionType.NotaryChange.Builder(originalState.state.notary)
val participants: Iterable<PublicKey> val participants: Iterable<AbstractParty>
if (state.encumbrance == null) { if (state.encumbrance == null) {
val modifiedState = TransactionState(state.data, modification) val modifiedState = TransactionState(state.data, modification)
@ -53,14 +54,14 @@ class NotaryChangeFlow<out T : ContractState>(
* *
* @return union of all added states' participants * @return union of all added states' participants
*/ */
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<PublicKey> { private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<AbstractParty> {
val stateRef = originalState.ref val stateRef = originalState.ref
val txId = stateRef.txhash val txId = stateRef.txhash
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId) val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId)
?: throw StateReplacementException("Transaction $txId not found") ?: throw StateReplacementException("Transaction $txId not found")
val outputs = issuingTx.tx.outputs val outputs = issuingTx.tx.outputs
val participants = mutableSetOf<PublicKey>() val participants = mutableSetOf<AbstractParty>()
var nextStateIndex = stateRef.index var nextStateIndex = stateRef.index
var newOutputPosition = tx.outputStates().size var newOutputPosition = tx.outputStates().size

View File

@ -1,6 +1,7 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.ALICE_PUBKEY import net.corda.testing.ALICE_PUBKEY
import org.junit.Test import org.junit.Test
@ -14,7 +15,7 @@ class DummyContractV2Tests {
@Test @Test
fun `upgrade from v1`() { fun `upgrade from v1`() {
val contractUpgrade = DummyContractV2() val contractUpgrade = DummyContractV2()
val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), DUMMY_NOTARY) val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY)
val v1Ref = StateRef(SecureHash.randomSHA256(), 0) val v1Ref = StateRef(SecureHash.randomSHA256(), 0)
val v1StateAndRef = StateAndRef(v1State, v1Ref) val v1StateAndRef = StateAndRef(v1State, v1Ref)
val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef) val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef)

View File

@ -2,13 +2,12 @@ package net.corda.core.contracts
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.utilities.DUMMY_PUBKEY_1 import net.corda.core.identity.AbstractParty
import net.corda.core.utilities.DUMMY_PUBKEY_2
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP
import net.corda.testing.ledger import net.corda.testing.ledger
import net.corda.testing.transaction import net.corda.testing.transaction
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
@ -19,9 +18,9 @@ class TransactionEncumbranceTests {
val state = Cash.State( val state = Cash.State(
amount = 1000.DOLLARS `issued by` defaultIssuer, amount = 1000.DOLLARS `issued by` defaultIssuer,
owner = DUMMY_PUBKEY_1 owner = MEGA_CORP
) )
val stateWithNewOwner = state.copy(owner = DUMMY_PUBKEY_2) val stateWithNewOwner = state.copy(owner = MINI_CORP)
val FOUR_PM: Instant = Instant.parse("2015-04-17T16:00:00.00Z") val FOUR_PM: Instant = Instant.parse("2015-04-17T16:00:00.00Z")
val FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS) val FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS)
@ -40,7 +39,7 @@ class TransactionEncumbranceTests {
data class State( data class State(
val validFrom: Instant val validFrom: Instant
) : ContractState { ) : ContractState {
override val participants: List<PublicKey> = emptyList() override val participants: List<AbstractParty> = emptyList()
override val contract: Contract = TEST_TIMELOCK_ID override val contract: Contract = TEST_TIMELOCK_ID
} }
} }
@ -52,7 +51,7 @@ class TransactionEncumbranceTests {
input { state } input { state }
output(encumbrance = 1) { stateWithNewOwner } output(encumbrance = 1) { stateWithNewOwner }
output("5pm time-lock") { timeLock } output("5pm time-lock") { timeLock }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
verifies() verifies()
} }
} }
@ -70,7 +69,7 @@ class TransactionEncumbranceTests {
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
input("5pm time-lock") input("5pm time-lock")
output { stateWithNewOwner } output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FIVE_PM) timestamp(FIVE_PM)
verifies() verifies()
} }
@ -89,7 +88,7 @@ class TransactionEncumbranceTests {
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
input("5pm time-lock") input("5pm time-lock")
output { state } output { state }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FOUR_PM) timestamp(FOUR_PM)
this `fails with` "the time specified in the time-lock has passed" this `fails with` "the time specified in the time-lock has passed"
} }
@ -106,7 +105,7 @@ class TransactionEncumbranceTests {
transaction { transaction {
input("state encumbered by 5pm time-lock") input("state encumbered by 5pm time-lock")
output { stateWithNewOwner } output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FIVE_PM) timestamp(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT" this `fails with` "Missing required encumbrance 1 in INPUT"
} }
@ -118,7 +117,7 @@ class TransactionEncumbranceTests {
transaction { transaction {
input { state } input { state }
output(encumbrance = 0) { stateWithNewOwner } output(encumbrance = 0) { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 0 in OUTPUT" this `fails with` "Missing required encumbrance 0 in OUTPUT"
} }
} }
@ -129,7 +128,7 @@ class TransactionEncumbranceTests {
input { state } input { state }
output(encumbrance = 2) { stateWithNewOwner } output(encumbrance = 2) { stateWithNewOwner }
output { timeLock } output { timeLock }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
this `fails with` "Missing required encumbrance 2 in OUTPUT" this `fails with` "Missing required encumbrance 2 in OUTPUT"
} }
} }
@ -146,7 +145,7 @@ class TransactionEncumbranceTests {
input("state encumbered by some other state") input("state encumbered by some other state")
input("5pm time-lock") input("5pm time-lock")
output { stateWithNewOwner } output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FIVE_PM) timestamp(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT" this `fails with` "Missing required encumbrance 1 in INPUT"
} }

View File

@ -11,7 +11,6 @@ import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.testing.ALICE_PUBKEY
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -95,7 +94,7 @@ class TransactionTests {
@Test @Test
fun `transactions with no inputs can have any notary`() { fun `transactions with no inputs can have any notary`() {
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), DUMMY_NOTARY) val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY)
val inputs = emptyList<StateAndRef<*>>() val inputs = emptyList<StateAndRef<*>>()
val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB)) val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB))
val commands = emptyList<AuthenticatedObject<CommandData>>() val commands = emptyList<AuthenticatedObject<CommandData>>()
@ -120,7 +119,7 @@ class TransactionTests {
@Test @Test
fun `transaction verification fails for duplicate inputs`() { fun `transaction verification fails for duplicate inputs`() {
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), DUMMY_NOTARY) val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY)
val stateRef = StateRef(SecureHash.randomSHA256(), 0) val stateRef = StateRef(SecureHash.randomSHA256(), 0)
val stateAndRef = StateAndRef(baseOutState, stateRef) val stateAndRef = StateAndRef(baseOutState, stateRef)
val inputs = listOf(stateAndRef, stateAndRef) val inputs = listOf(stateAndRef, stateAndRef)
@ -148,7 +147,7 @@ class TransactionTests {
@Test @Test
fun `general transactions cannot change notary`() { fun `general transactions cannot change notary`() {
val notary: Party = DUMMY_NOTARY val notary: Party = DUMMY_NOTARY
val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), notary) val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary)
val outState = inState.copy(notary = ALICE) val outState = inState.copy(notary = ALICE)
val inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0))) val inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0)))
val outputs = listOf(outState) val outputs = listOf(outState)

View File

@ -12,9 +12,7 @@ import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_PUBKEY_1 import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.core.utilities.TEST_TX_TIME import net.corda.core.utilities.TEST_TX_TIME
import net.corda.testing.MEGA_CORP import net.corda.testing.*
import net.corda.testing.MEGA_CORP_PUBKEY
import net.corda.testing.ledger
import org.junit.Test import org.junit.Test
import java.security.PublicKey import java.security.PublicKey
import kotlin.test.* import kotlin.test.*
@ -30,20 +28,20 @@ class PartialMerkleTreeTest {
output("MEGA_CORP cash") { output("MEGA_CORP cash") {
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP_PUBKEY owner = MEGA_CORP
) )
} }
output("dummy cash 1") { output("dummy cash 1") {
Cash.State( Cash.State(
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = DUMMY_PUBKEY_1 owner = MINI_CORP
) )
} }
} }
transaction { transaction {
input("MEGA_CORP cash") input("MEGA_CORP cash")
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1)) output("MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies() this.verifies()
@ -98,7 +96,7 @@ class PartialMerkleTreeTest {
fun filtering(elem: Any): Boolean { fun filtering(elem: Any): Boolean {
return when (elem) { return when (elem) {
is StateRef -> true is StateRef -> true
is TransactionState<*> -> elem.data.participants[0].keys == DUMMY_PUBKEY_1.keys is TransactionState<*> -> elem.data.participants[0].owningKey.keys == MINI_CORP_PUBKEY.keys
is Command -> MEGA_CORP_PUBKEY in elem.signers is Command -> MEGA_CORP_PUBKEY in elem.signers
is Timestamp -> true is Timestamp -> true
is PublicKey -> elem == MEGA_CORP_PUBKEY is PublicKey -> elem == MEGA_CORP_PUBKEY

View File

@ -88,7 +88,7 @@ class CollectSignaturesFlowTests {
val state = receive<DummyContract.MultiOwnerState>(otherParty).unwrap { it } val state = receive<DummyContract.MultiOwnerState>(otherParty).unwrap { it }
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
val command = Command(DummyContract.Commands.Create(), state.participants) val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey })
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command) val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false) val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
val stx = subFlow(CollectSignaturesFlow(ptx)) val stx = subFlow(CollectSignaturesFlow(ptx))
@ -108,7 +108,7 @@ class CollectSignaturesFlowTests {
@Suspendable @Suspendable
override fun call(): SignedTransaction { override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
val command = Command(DummyContract.Commands.Create(), state.participants) val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey })
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command) val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false) val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
val stx = subFlow(CollectSignaturesFlow(ptx)) val stx = subFlow(CollectSignaturesFlow(ptx))
@ -142,7 +142,7 @@ class CollectSignaturesFlowTests {
fun `successfully collects two signatures`() { fun `successfully collects two signatures`() {
val magicNumber = 1337 val magicNumber = 1337
val parties = listOf(a.info.legalIdentity, b.info.legalIdentity, c.info.legalIdentity) val parties = listOf(a.info.legalIdentity, b.info.legalIdentity, c.info.legalIdentity)
val state = DummyContract.MultiOwnerState(magicNumber, parties.map { it.owningKey }) val state = DummyContract.MultiOwnerState(magicNumber, parties)
val flow = a.services.startFlow(TestFlowTwo.Initiator(state, b.info.legalIdentity)) val flow = a.services.startFlow(TestFlowTwo.Initiator(state, b.info.legalIdentity))
mockNet.runNetwork() mockNet.runNetwork()
val result = flow.resultFuture.getOrThrow() val result = flow.resultFuture.getOrThrow()

View File

@ -5,6 +5,7 @@ import net.corda.contracts.asset.Cash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -27,7 +28,6 @@ import net.corda.testing.startRpcClient
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -187,21 +187,21 @@ class ContractUpgradeFlowTest {
val firstState = a.database.transaction { a.vault.unconsumedStates<ContractState>().single() } val firstState = a.database.transaction { a.vault.unconsumedStates<ContractState>().single() }
assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.") assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.")
assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.") assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.")
assertEquals(listOf(a.info.legalIdentity.owningKey), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.") assertEquals<Collection<AbstractParty>>(listOf(a.info.legalIdentity), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")
} }
class CashV2 : UpgradedContract<Cash.State, CashV2.State> { class CashV2 : UpgradedContract<Cash.State, CashV2.State> {
override val legacyContract = Cash::class.java override val legacyContract = Cash::class.java
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<PublicKey>) : FungibleAsset<Currency> { data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
override val owner: PublicKey = owners.first() override val owner: AbstractParty = owners.first()
override val exitKeys = (owners + amount.token.issuer.party.owningKey).toSet() override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet()
override val contract = CashV2() override val contract = CashV2()
override val participants = owners override val participants = owners
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKey) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner)) override fun move(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
override fun toString() = "${Emoji.bagOfCash}New Cash($amount at ${amount.token.issuer} owned by $owner)" override fun toString() = "${Emoji.bagOfCash}New Cash($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: PublicKey) = Pair(Cash.Commands.Move(), copy(owners = listOf(newOwner))) override fun withNewOwner(newOwner: AbstractParty) = Pair(Cash.Commands.Move(), copy(owners = listOf(newOwner)))
} }
override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner)) override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner))

View File

@ -12,7 +12,7 @@ import net.corda.flows.ResolveTransactionsFlow
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MINI_CORP_PUBKEY import net.corda.testing.MINI_CORP
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -94,7 +94,7 @@ class ResolveTransactionsFlowTest {
val count = 50 val count = 50
var cursor = stx2 var cursor = stx2
repeat(count) { repeat(count) {
val stx = DummyContract.move(cursor.tx.outRef(0), MINI_CORP_PUBKEY) val stx = DummyContract.move(cursor.tx.outRef(0), MINI_CORP)
.addSignatureUnchecked(NullSignature) .addSignatureUnchecked(NullSignature)
.toSignedTransaction(false) .toSignedTransaction(false)
a.database.transaction { a.database.transaction {
@ -113,13 +113,13 @@ class ResolveTransactionsFlowTest {
fun `triangle of transactions resolves fine`() { fun `triangle of transactions resolves fine`() {
val stx1 = makeTransactions().first val stx1 = makeTransactions().first
val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP_PUBKEY).run { val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP).run {
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
toSignedTransaction() toSignedTransaction()
} }
val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP_PUBKEY).run { val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP).run {
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
toSignedTransaction() toSignedTransaction()
@ -173,7 +173,7 @@ class ResolveTransactionsFlowTest {
it.signWith(DUMMY_NOTARY_KEY) it.signWith(DUMMY_NOTARY_KEY)
it.toSignedTransaction(false) it.toSignedTransaction(false)
} }
val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP_PUBKEY).let { val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP).let {
it.signWith(MEGA_CORP_KEY) it.signWith(MEGA_CORP_KEY)
it.signWith(DUMMY_NOTARY_KEY) it.signWith(DUMMY_NOTARY_KEY)
it.toSignedTransaction() it.toSignedTransaction()

View File

@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.StorageService import net.corda.core.node.services.StorageService
@ -53,7 +54,7 @@ class AttachmentClassLoaderTests {
class AttachmentDummyContract : Contract { class AttachmentDummyContract : Contract {
data class State(val magicNumber: Int = 0) : ContractState { data class State(val magicNumber: Int = 0) : ContractState {
override val contract = ATTACHMENT_TEST_PROGRAM_ID override val contract = ATTACHMENT_TEST_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = listOf() get() = listOf()
} }

View File

@ -2,10 +2,10 @@ package net.corda.core.node
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -20,7 +20,7 @@ class VaultUpdateTests {
} }
private class DummyState : ContractState { private class DummyState : ContractState {
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = emptyList() get() = emptyList()
override val contract = VaultUpdateTests.DummyContract override val contract = VaultUpdateTests.DummyContract
} }

View File

@ -2,14 +2,19 @@ package net.corda.core.serialization
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.* import net.corda.core.utilities.DUMMY_KEY_2
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.TEST_TX_TIME
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.generateStateRef import net.corda.testing.generateStateRef
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -27,12 +32,12 @@ class TransactionSerializationTests {
data class State( data class State(
val deposit: PartyAndReference, val deposit: PartyAndReference,
val amount: Amount<Currency>, val amount: Amount<Currency>,
override val owner: PublicKey) : OwnableState { override val owner: AbstractParty) : OwnableState {
override val contract: Contract = TEST_PROGRAM_ID override val contract: Contract = TEST_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -44,9 +49,9 @@ class TransactionSerializationTests {
// It refers to a fake TX/state that we don't bother creating here. // It refers to a fake TX/state that we don't bother creating here.
val depositRef = MINI_CORP.ref(1) val depositRef = MINI_CORP.ref(1)
val fakeStateRef = generateStateRef() val fakeStateRef = generateStateRef()
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY), fakeStateRef) val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), DUMMY_NOTARY), fakeStateRef)
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY) val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY)
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public), DUMMY_NOTARY) val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY)
lateinit var tx: TransactionBuilder lateinit var tx: TransactionBuilder
@ -54,14 +59,14 @@ class TransactionSerializationTests {
@Before @Before
fun setup() { fun setup() {
tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems( tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems(
inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(DUMMY_KEY_1.public)) inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(MEGA_CORP.owningKey))
) )
} }
@Test @Test
fun signWireTX() { fun signWireTX() {
tx.signWith(DUMMY_NOTARY_KEY) tx.signWith(DUMMY_NOTARY_KEY)
tx.signWith(DUMMY_KEY_1) tx.signWith(MEGA_CORP_KEY)
val signedTX = tx.toSignedTransaction() val signedTX = tx.toSignedTransaction()
// Now check that the signature we just made verifies. // Now check that the signature we just made verifies.
@ -81,7 +86,7 @@ class TransactionSerializationTests {
tx.toSignedTransaction() tx.toSignedTransaction()
} }
tx.signWith(DUMMY_KEY_1) tx.signWith(MEGA_CORP_KEY)
tx.signWith(DUMMY_NOTARY_KEY) tx.signWith(DUMMY_NOTARY_KEY)
val signedTX = tx.toSignedTransaction() val signedTX = tx.toSignedTransaction()
@ -104,7 +109,7 @@ class TransactionSerializationTests {
@Test @Test
fun timestamp() { fun timestamp() {
tx.setTime(TEST_TX_TIME, 30.seconds) tx.setTime(TEST_TX_TIME, 30.seconds)
tx.signWith(DUMMY_KEY_1) tx.signWith(MEGA_CORP_KEY)
tx.signWith(DUMMY_NOTARY_KEY) tx.signWith(DUMMY_NOTARY_KEY)
val stx = tx.toSignedTransaction() val stx = tx.toSignedTransaction()
assertEquals(TEST_TX_TIME, stx.tx.timestamp?.midpoint) assertEquals(TEST_TX_TIME, stx.tx.timestamp?.midpoint)

View File

@ -38,10 +38,9 @@ UNRELEASED
* ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for * ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for
backwards compatibility, but it will be removed in a future release and developers should move to the new class as soon backwards compatibility, but it will be removed in a future release and developers should move to the new class as soon
as possible. as possible.
* There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. A new class * There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. This now replaces
``AnonymousParty`` has been added, which is intended to be used in place of ``Party`` or ``PublicKey`` in contract use of ``Party`` and ``PublicKey`` in state objects, and allows use of full or anonymised parties depending on
state objects. The exception to this is where the party in a contract state is intended to be well known, such as use-case.
issuer of a ``Cash`` state.
* Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the * Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the
name. As a result all node legal names must now be structured as X.500 distinguished names. name. As a result all node legal names must now be structured as X.500 distinguished names.

View File

@ -48,12 +48,12 @@ private fun gatherOurInputs(serviceHub: ServiceHub,
notary: Party?): Pair<List<StateAndRef<Cash.State>>, Long> { notary: Party?): Pair<List<StateAndRef<Cash.State>>, Long> {
// Collect cash type inputs // Collect cash type inputs
val cashStates = serviceHub.vaultService.unconsumedStates<Cash.State>() val cashStates = serviceHub.vaultService.unconsumedStates<Cash.State>()
// extract our key identity for convenience // extract our identity for convenience
val ourKey = serviceHub.myInfo.legalIdentity.owningKey val ourIdentity = serviceHub.myInfo.legalIdentity
// Filter down to our own cash states with right currency and issuer // Filter down to our own cash states with right currency and issuer
val suitableCashStates = cashStates.filter { val suitableCashStates = cashStates.filter {
val state = it.state.data val state = it.state.data
(state.owner == ourKey) (state.owner == ourIdentity)
&& (state.amount.token == amountRequired.token) && (state.amount.token == amountRequired.token)
} }
require(!suitableCashStates.isEmpty()) { "Insufficient funds" } require(!suitableCashStates.isEmpty()) { "Insufficient funds" }
@ -90,12 +90,12 @@ private fun prepareOurInputsAndOutputs(serviceHub: ServiceHub, request: FxReques
val (inputs, residual) = gatherOurInputs(serviceHub, sellAmount, request.notary) val (inputs, residual) = gatherOurInputs(serviceHub, sellAmount, request.notary)
// Build and an output state for the counterparty // Build and an output state for the counterparty
val transferedFundsOutput = Cash.State(sellAmount, request.counterparty.owningKey) val transferedFundsOutput = Cash.State(sellAmount, request.counterparty)
if (residual > 0L) { if (residual > 0L) {
// Build an output state for the residual change back to us // Build an output state for the residual change back to us
val residualAmount = Amount(residual, sellAmount.token) val residualAmount = Amount(residual, sellAmount.token)
val residualOutput = Cash.State(residualAmount, serviceHub.myInfo.legalIdentity.owningKey) val residualOutput = Cash.State(residualAmount, serviceHub.myInfo.legalIdentity)
return FxResponse(inputs, listOf(transferedFundsOutput, residualOutput)) return FxResponse(inputs, listOf(transferedFundsOutput, residualOutput))
} else { } else {
return FxResponse(inputs, listOf(transferedFundsOutput)) return FxResponse(inputs, listOf(transferedFundsOutput))
@ -140,7 +140,7 @@ class ForeignExchangeFlow(val tradeId: String,
require(it.inputs.all { it.state.notary == notary }) { require(it.inputs.all { it.state.notary == notary }) {
"notary of remote states must be same as for our states" "notary of remote states must be same as for our states"
} }
require(it.inputs.all { it.state.data.owner == remoteRequestWithNotary.owner.owningKey }) { require(it.inputs.all { it.state.data.owner == remoteRequestWithNotary.owner }) {
"The inputs are not owned by the correct counterparty" "The inputs are not owned by the correct counterparty"
} }
require(it.inputs.all { it.state.data.amount.token == remoteRequestWithNotary.amount.token }) { require(it.inputs.all { it.state.data.amount.token == remoteRequestWithNotary.amount.token }) {
@ -153,7 +153,7 @@ class ForeignExchangeFlow(val tradeId: String,
>= remoteRequestWithNotary.amount.quantity) { >= remoteRequestWithNotary.amount.quantity) {
"the provided inputs don't provide sufficient funds" "the provided inputs don't provide sufficient funds"
} }
require(it.outputs.filter { it.owner == serviceHub.myInfo.legalIdentity.owningKey }. require(it.outputs.filter { it.owner == serviceHub.myInfo.legalIdentity }.
map { it.amount.quantity }.sum() == remoteRequestWithNotary.amount.quantity) { map { it.amount.quantity }.sum() == remoteRequestWithNotary.amount.quantity) {
"the provided outputs don't provide the request quantity" "the provided outputs don't provide the request quantity"
} }
@ -195,8 +195,8 @@ class ForeignExchangeFlow(val tradeId: String,
val builder = TransactionType.General.Builder(ourStates.inputs.first().state.notary) val builder = TransactionType.General.Builder(ourStates.inputs.first().state.notary)
// Add the move commands and key to indicate all the respective owners and need to sign // Add the move commands and key to indicate all the respective owners and need to sign
val ourSigners = ourStates.inputs.map { it.state.data.owner }.toSet() val ourSigners = ourStates.inputs.map { it.state.data.owner.owningKey }.toSet()
val theirSigners = theirStates.inputs.map { it.state.data.owner }.toSet() val theirSigners = theirStates.inputs.map { it.state.data.owner.owningKey }.toSet()
builder.addCommand(Cash.Commands.Move(), (ourSigners + theirSigners).toList()) builder.addCommand(Cash.Commands.Move(), (ourSigners + theirSigners).toList())
// Build and add the inputs and outputs // Build and add the inputs and outputs

View File

@ -2,9 +2,13 @@ package net.corda.docs
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny
import net.corda.core.crypto.sign
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
@ -64,10 +68,10 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState { override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState {
val parties: List<Party> get() = listOf(source, counterparty) val parties: List<Party> get() = listOf(source, counterparty)
override val participants: List<PublicKey> get() = parties.map { it.owningKey } override val participants: List<AbstractParty> get() = parties
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { it.containsAny(ourKeys) } return participants.any { it.owningKey.containsAny(ourKeys) }
} }
} }

View File

@ -10,7 +10,8 @@ We've added the ability for flows to be versioned by their CorDapp developers. T
version of a flow and allows it to reject flow communication with a node which isn't using the same fact. In a future version of a flow and allows it to reject flow communication with a node which isn't using the same fact. In a future
release we allow a node to have multiple versions of the same flow running to enable backwards compatibility. release we allow a node to have multiple versions of the same flow running to enable backwards compatibility.
There are major changes to the ``Party`` class as part of confidential identities. See :doc:`changelog` for full details. There are major changes to the ``Party`` class as part of confidential identities, and how parties and keys are stored
in transaction state objects. See :doc:`changelog` for full details.
Milestone 11 Milestone 11

View File

@ -2,16 +2,16 @@ package net.corda.contracts.universal
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey
import java.time.Instant import java.time.Instant
val UNIVERSAL_PROGRAM_ID = UniversalContract() val UNIVERSAL_PROGRAM_ID = UniversalContract()
class UniversalContract : Contract { class UniversalContract : Contract {
data class State(override val participants: List<PublicKey>, data class State(override val participants: List<AbstractParty>,
val details: Arrangement) : ContractState { val details: Arrangement) : ContractState {
override val contract = UNIVERSAL_PROGRAM_ID override val contract = UNIVERSAL_PROGRAM_ID
} }
@ -316,7 +316,7 @@ class UniversalContract : Contract {
override val legalContractReference: SecureHash override val legalContractReference: SecureHash
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKey) { fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: Party) {
check(tx.inputStates().isEmpty()) check(tx.inputStates().isEmpty())
tx.addOutputState(State(listOf(notary), arrangement)) tx.addOutputState(State(listOf(notary), arrangement))
tx.addCommand(Commands.Issue(), at.party.owningKey) tx.addCommand(Commands.Issue(), at.party.owningKey)

View File

@ -130,15 +130,15 @@ class Cap {
val paymentFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) } val paymentFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial) val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFirst) val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFirst)
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterExecutionFirst) val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterExecutionFirst)
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst) val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFirst)
val stateAfterFixingFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFinal) val stateAfterFixingFinal = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFinal)
val statePaymentFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFinal) val statePaymentFinal = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFinal)
val contractLimitedCap = arrange { val contractLimitedCap = arrange {
rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.SemiAnnual, object { rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.SemiAnnual, object {

View File

@ -44,11 +44,11 @@ class Caplet {
val contractFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) } val contractFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract) val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
val stateFixed = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractFixed) val stateFixed = UniversalContract.State(listOf(DUMMY_NOTARY), contractFixed)
val stateFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractFinal) val stateFinal = UniversalContract.State(listOf(DUMMY_NOTARY), contractFinal)
@Test @Test
fun issue() { fun issue() {

View File

@ -43,9 +43,9 @@ class FXFwdTimeOption
val TEST_TX_TIME_BEFORE_MATURITY: Instant get() = Instant.parse("2018-05-01T12:00:00.00Z") val TEST_TX_TIME_BEFORE_MATURITY: Instant get() = Instant.parse("2018-05-01T12:00:00.00Z")
val TEST_TX_TIME_AFTER_MATURITY: Instant get() = Instant.parse("2018-06-02T12:00:00.00Z") val TEST_TX_TIME_AFTER_MATURITY: Instant get() = Instant.parse("2018-06-02T12:00:00.00Z")
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), initialContract) val inState = UniversalContract.State(listOf(DUMMY_NOTARY), initialContract)
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract1) val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY), outContract1)
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract2) val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY), outContract2)
@Test @Test
fun `issue - signature`() { fun `issue - signature`() {

View File

@ -25,18 +25,18 @@ class FXSwap {
val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, EUR) } val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, EUR) }
val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, USD) } val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, USD) }
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer1) val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY), transfer1)
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer2) val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY), transfer2)
val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, USD) } // wrong currency val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, USD) } // wrong currency
val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, USD) } // wrong amount val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, USD) } // wrong amount
val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1070.K, EUR) } // wrong party val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1070.K, EUR) } // wrong party
val outStateBad1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad1) val outStateBad1 = UniversalContract.State(listOf(DUMMY_NOTARY), transferBad1)
val outStateBad2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad2) val outStateBad2 = UniversalContract.State(listOf(DUMMY_NOTARY), transferBad2)
val outStateBad3 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad3) val outStateBad3 = UniversalContract.State(listOf(DUMMY_NOTARY), transferBad3)
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract) val inState = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
@Test @Test
fun `issue - signature`() { fun `issue - signature`() {

View File

@ -122,12 +122,12 @@ class IRS {
val paymentFirst = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) } val paymentFirst = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial) val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFirst) val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFirst)
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterExecutionFirst) val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterExecutionFirst)
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst) val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFirst)
@Test @Test

View File

@ -36,7 +36,7 @@ class RollOutTests {
} }
} }
} }
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract) val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
val contractStep1a = arrange { val contractStep1a = arrange {
rollOut("2016-10-03".ld, "2017-09-01".ld, Frequency.Monthly) { rollOut("2016-10-03".ld, "2017-09-01".ld, Frequency.Monthly) {
@ -55,8 +55,8 @@ class RollOutTests {
highStreetBank.owes(acmeCorp, 10.K, USD) highStreetBank.owes(acmeCorp, 10.K, USD)
} }
val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1a) val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY), contractStep1a)
val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1b) val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY), contractStep1b)
val contract_transfer1 = arrange { val contract_transfer1 = arrange {
highStreetBank.owes(acmeCorp, 10.K, USD) highStreetBank.owes(acmeCorp, 10.K, USD)

View File

@ -54,7 +54,7 @@ class Swaption {
} }
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial) val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
@Test @Test
fun issue() { fun issue() {

View File

@ -33,12 +33,12 @@ class ZeroCouponBond {
val transfer = arrange { highStreetBank.owes(acmeCorp, 100.K, GBP) } val transfer = arrange { highStreetBank.owes(acmeCorp, 100.K, GBP) }
val transferWrong = arrange { highStreetBank.owes(acmeCorp, 80.K, GBP) } val transferWrong = arrange { highStreetBank.owes(acmeCorp, 80.K, GBP) }
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract) val inState = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
val outState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer) val outState = UniversalContract.State(listOf(DUMMY_NOTARY), transfer)
val outStateWrong = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferWrong) val outStateWrong = UniversalContract.State(listOf(DUMMY_NOTARY), transferWrong)
val outStateMove = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractMove) val outStateMove = UniversalContract.State(listOf(DUMMY_NOTARY), contractMove)
@Test @Test
fun basic() { fun basic() {

View File

@ -3,6 +3,7 @@ package net.corda.contracts.isolated
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey import java.security.PublicKey
@ -13,7 +14,7 @@ val ANOTHER_DUMMY_PROGRAM_ID = AnotherDummyContract()
class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor { class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor {
data class State(val magicNumber: Int = 0) : ContractState { data class State(val magicNumber: Int = 0) : ContractState {
override val contract = ANOTHER_DUMMY_PROGRAM_ID override val contract = ANOTHER_DUMMY_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = emptyList() get() = emptyList()
} }

View File

@ -1,6 +1,7 @@
package net.corda.contracts; package net.corda.contracts;
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.identity.AbstractParty;
import java.security.PublicKey; import java.security.PublicKey;
import java.time.*; import java.time.*;
@ -12,7 +13,7 @@ import java.util.*;
* ultimately either language can be used against a common test framework (and therefore can be used for real). * ultimately either language can be used against a common test framework (and therefore can be used for real).
*/ */
public interface ICommercialPaperState extends ContractState { public interface ICommercialPaperState extends ContractState {
ICommercialPaperState withOwner(PublicKey newOwner); ICommercialPaperState withOwner(AbstractParty newOwner);
ICommercialPaperState withFaceValue(Amount<Issued<Currency>> newFaceValue); ICommercialPaperState withFaceValue(Amount<Issued<Currency>> newFaceValue);

View File

@ -9,6 +9,9 @@ import net.corda.core.contracts.Contract;
import net.corda.core.contracts.TransactionForContract.*; import net.corda.core.contracts.TransactionForContract.*;
import net.corda.core.contracts.clauses.*; import net.corda.core.contracts.clauses.*;
import net.corda.core.crypto.*; import net.corda.core.crypto.*;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.AnonymousParty;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.node.services.*; import net.corda.core.node.services.*;
import net.corda.core.transactions.*; import net.corda.core.transactions.*;
@ -34,14 +37,14 @@ public class JavaCommercialPaper implements Contract {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class State implements OwnableState, ICommercialPaperState { public static class State implements OwnableState, ICommercialPaperState {
private PartyAndReference issuance; private PartyAndReference issuance;
private PublicKey owner; private AbstractParty owner;
private Amount<Issued<Currency>> faceValue; private Amount<Issued<Currency>> faceValue;
private Instant maturityDate; private Instant maturityDate;
public State() { public State() {
} // For serialization } // For serialization
public State(PartyAndReference issuance, PublicKey owner, Amount<Issued<Currency>> faceValue, public State(PartyAndReference issuance, AbstractParty owner, Amount<Issued<Currency>> faceValue,
Instant maturityDate) { Instant maturityDate) {
this.issuance = issuance; this.issuance = issuance;
this.owner = owner; this.owner = owner;
@ -53,13 +56,13 @@ public class JavaCommercialPaper implements Contract {
return new State(this.issuance, this.owner, this.faceValue, this.maturityDate); return new State(this.issuance, this.owner, this.faceValue, this.maturityDate);
} }
public ICommercialPaperState withOwner(PublicKey newOwner) { public ICommercialPaperState withOwner(AbstractParty newOwner) {
return new State(this.issuance, newOwner, this.faceValue, this.maturityDate); return new State(this.issuance, newOwner, this.faceValue, this.maturityDate);
} }
@NotNull @NotNull
@Override @Override
public Pair<CommandData, OwnableState> withNewOwner(@NotNull PublicKey newOwner) { public Pair<CommandData, OwnableState> withNewOwner(@NotNull AbstractParty newOwner) {
return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate)); return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
} }
@ -76,7 +79,7 @@ public class JavaCommercialPaper implements Contract {
} }
@NotNull @NotNull
public PublicKey getOwner() { public AbstractParty getOwner() {
return owner; return owner;
} }
@ -117,12 +120,12 @@ public class JavaCommercialPaper implements Contract {
} }
public State withoutOwner() { public State withoutOwner() {
return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate); return new State(issuance, new AnonymousParty(NullPublicKey.INSTANCE), faceValue, maturityDate);
} }
@NotNull @NotNull
@Override @Override
public List<PublicKey> getParticipants() { public List<AbstractParty> getParticipants() {
return ImmutableList.of(this.owner); return ImmutableList.of(this.owner);
} }
} }
@ -162,12 +165,12 @@ public class JavaCommercialPaper implements Contract {
@NotNull List<? extends State> inputs, @NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs, @NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands, @NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
@NotNull State groupingKey) { State groupingKey) {
AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class); AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
// There should be only a single input due to aggregation above // There should be only a single input due to aggregation above
State input = single(inputs); State input = single(inputs);
if (!cmd.getSigners().contains(input.getOwner())) if (!cmd.getSigners().contains(input.getOwner().getOwningKey()))
throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP"); throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP");
// Check the output CP state is the same as the input state, ignoring the owner field. // Check the output CP state is the same as the input state, ignoring the owner field.
@ -194,13 +197,13 @@ public class JavaCommercialPaper implements Contract {
@NotNull List<? extends State> inputs, @NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs, @NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands, @NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
@NotNull State groupingKey) { State groupingKey) {
AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class); AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class);
// There should be only a single input due to aggregation above // There should be only a single input due to aggregation above
State input = single(inputs); State input = single(inputs);
if (!cmd.getSigners().contains(input.getOwner())) if (!cmd.getSigners().contains(input.getOwner().getOwningKey()))
throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP"); throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP");
Timestamp timestamp = tx.getTimestamp(); Timestamp timestamp = tx.getTimestamp();
@ -237,7 +240,7 @@ public class JavaCommercialPaper implements Contract {
@NotNull List<? extends State> inputs, @NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs, @NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands, @NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
@NotNull State groupingKey) { State groupingKey) {
AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class); AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class);
State output = single(outputs); State output = single(outputs);
Timestamp timestampCommand = tx.getTimestamp(); Timestamp timestampCommand = tx.getTimestamp();
@ -304,7 +307,7 @@ public class JavaCommercialPaper implements Contract {
} }
public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) { public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) {
State state = new State(issuance, issuance.getParty().getOwningKey(), faceValue, maturityDate); State state = new State(issuance, issuance.getParty(), faceValue, maturityDate);
TransactionState output = new TransactionState<>(state, notary, encumbrance); TransactionState output = new TransactionState<>(state, notary, encumbrance);
return new TransactionType.General.Builder(notary).withItems(output, new Command(new Commands.Issue(), issuance.getParty().getOwningKey())); return new TransactionType.General.Builder(notary).withItems(output, new Command(new Commands.Issue(), issuance.getParty().getOwningKey()));
} }
@ -317,12 +320,12 @@ public class JavaCommercialPaper implements Contract {
public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, VaultService vault) throws InsufficientBalanceException { public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, VaultService vault) throws InsufficientBalanceException {
vault.generateSpend(tx, StructuresKt.withoutIssuer(paper.getState().getData().getFaceValue()), paper.getState().getData().getOwner(), null); vault.generateSpend(tx, StructuresKt.withoutIssuer(paper.getState().getData().getFaceValue()), paper.getState().getData().getOwner(), null);
tx.addInputState(paper); tx.addInputState(paper);
tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner())); tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner().getOwningKey()));
} }
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKey newOwner) { public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, AbstractParty newOwner) {
tx.addInputState(paper); tx.addInputState(paper);
tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance())); tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance()));
tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner())); tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner().getOwningKey()));
} }
} }

View File

@ -10,6 +10,7 @@ import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.random63BitValue import net.corda.core.random63BitValue
@ -19,7 +20,6 @@ import net.corda.core.schemas.QueryableState
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import net.corda.schemas.CommercialPaperSchemaV1 import net.corda.schemas.CommercialPaperSchemaV1
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -61,22 +61,22 @@ class CommercialPaper : Contract {
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
override val owner: PublicKey, override val owner: AbstractParty,
val faceValue: Amount<Issued<Currency>>, val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant val maturityDate: Instant
) : OwnableState, QueryableState, ICommercialPaperState { ) : OwnableState, QueryableState, ICommercialPaperState {
override val contract = CP_PROGRAM_ID override val contract = CP_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = listOf(owner) get() = listOf(owner)
val token: Issued<Terms> val token: Issued<Terms>
get() = Issued(issuance, Terms(faceValue.token, maturityDate)) get() = Issued(issuance, Terms(faceValue.token, maturityDate))
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)" override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later, // Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner) override fun withOwner(newOwner: AbstractParty): ICommercialPaperState = copy(owner = newOwner)
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue) override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate) override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
@ -91,7 +91,7 @@ class CommercialPaper : Contract {
is CommercialPaperSchemaV1 -> CommercialPaperSchemaV1.PersistentCommercialPaperState( is CommercialPaperSchemaV1 -> CommercialPaperSchemaV1.PersistentCommercialPaperState(
issuanceParty = this.issuance.party.owningKey.toBase58String(), issuanceParty = this.issuance.party.owningKey.toBase58String(),
issuanceRef = this.issuance.reference.bytes, issuanceRef = this.issuance.reference.bytes,
owner = this.owner.toBase58String(), owner = this.owner.owningKey.toBase58String(),
maturity = this.maturityDate, maturity = this.maturityDate,
faceValue = this.faceValue.quantity, faceValue = this.faceValue.quantity,
currency = this.faceValue.token.product.currencyCode, currency = this.faceValue.token.product.currencyCode,
@ -146,7 +146,7 @@ class CommercialPaper : Contract {
val command = commands.requireSingleCommand<Commands.Move>() val command = commands.requireSingleCommand<Commands.Move>()
val input = inputs.single() val input = inputs.single()
requireThat { requireThat {
"the transaction is signed by the owner of the CP" using (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
"the state is propagated" using (outputs.size == 1) "the state is propagated" using (outputs.size == 1)
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to // Don't need to check anything else, as if outputs.size == 1 then the output is equal to
// the input ignoring the owner field due to the grouping. // the input ignoring the owner field due to the grouping.
@ -175,7 +175,7 @@ class CommercialPaper : Contract {
"the paper must have matured" using (time >= input.maturityDate) "the paper must have matured" using (time >= input.maturityDate)
"the received amount equals the face value" using (received == input.faceValue) "the received amount equals the face value" using (received == input.faceValue)
"the paper must be destroyed" using outputs.isEmpty() "the paper must be destroyed" using outputs.isEmpty()
"the transaction is signed by the owner of the CP" using (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
} }
return setOf(command.value) return setOf(command.value)
@ -196,17 +196,17 @@ class CommercialPaper : Contract {
* at the moment: this restriction is not fundamental and may be lifted later. * at the moment: this restriction is not fundamental and may be lifted later.
*/ */
fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant, notary: Party): TransactionBuilder { fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant, notary: Party): TransactionBuilder {
val state = TransactionState(State(issuance, issuance.party.owningKey, faceValue, maturityDate), notary) val state = TransactionState(State(issuance, issuance.party, faceValue, maturityDate), notary)
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey)) return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
} }
/** /**
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper. * Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
*/ */
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) { fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: AbstractParty) {
tx.addInputState(paper) tx.addInputState(paper)
tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary)) tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary))
tx.addCommand(Commands.Move(), paper.state.data.owner) tx.addCommand(Commands.Move(), paper.state.data.owner.owningKey)
} }
/** /**
@ -223,12 +223,12 @@ class CommercialPaper : Contract {
val amount = paper.state.data.faceValue.let { amount -> Amount(amount.quantity, amount.token.product) } val amount = paper.state.data.faceValue.let { amount -> Amount(amount.quantity, amount.token.product) }
vault.generateSpend(tx, amount, paper.state.data.owner) vault.generateSpend(tx, amount, paper.state.data.owner)
tx.addInputState(paper) tx.addInputState(paper)
tx.addCommand(CommercialPaper.Commands.Redeem(), paper.state.data.owner) tx.addCommand(CommercialPaper.Commands.Redeem(), paper.state.data.owner.owningKey)
} }
} }
infix fun CommercialPaper.State.`owned by`(owner: PublicKey) = copy(owner = owner) infix fun CommercialPaper.State.`owned by`(owner: AbstractParty) = copy(owner = owner)
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary) infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
infix fun ICommercialPaperState.`owned by`(newOwner: PublicKey) = withOwner(newOwner) infix fun ICommercialPaperState.`owned by`(newOwner: AbstractParty) = withOwner(newOwner)

View File

@ -3,13 +3,13 @@ package net.corda.contracts
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.sumCashBy import net.corda.contracts.asset.sumCashBy
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.NullPublicKey import net.corda.core.crypto.NULL_PARTY
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -27,19 +27,19 @@ class CommercialPaperLegacy : Contract {
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
override val owner: PublicKey, override val owner: AbstractParty,
val faceValue: Amount<Issued<Currency>>, val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant val maturityDate: Instant
) : OwnableState, ICommercialPaperState { ) : OwnableState, ICommercialPaperState {
override val contract = CP_LEGACY_PROGRAM_ID override val contract = CP_LEGACY_PROGRAM_ID
override val participants = listOf(owner) override val participants = listOf(owner)
fun withoutOwner() = copy(owner = NullPublicKey) fun withoutOwner() = copy(owner = NULL_PARTY)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)" override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later, // Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner) override fun withOwner(newOwner: AbstractParty): ICommercialPaperState = copy(owner = newOwner)
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue) override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate) override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
@ -70,7 +70,7 @@ class CommercialPaperLegacy : Contract {
is Commands.Move -> { is Commands.Move -> {
val input = inputs.single() val input = inputs.single()
requireThat { requireThat {
"the transaction is signed by the owner of the CP" using (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
"the state is propagated" using (outputs.size == 1) "the state is propagated" using (outputs.size == 1)
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to // Don't need to check anything else, as if outputs.size == 1 then the output is equal to
// the input ignoring the owner field due to the grouping. // the input ignoring the owner field due to the grouping.
@ -86,7 +86,7 @@ class CommercialPaperLegacy : Contract {
"the paper must have matured" using (time >= input.maturityDate) "the paper must have matured" using (time >= input.maturityDate)
"the received amount equals the face value" using (received == input.faceValue) "the received amount equals the face value" using (received == input.faceValue)
"the paper must be destroyed" using outputs.isEmpty() "the paper must be destroyed" using outputs.isEmpty()
"the transaction is signed by the owner of the CP" using (input.owner in command.signers) "the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
} }
} }
@ -114,14 +114,14 @@ class CommercialPaperLegacy : Contract {
fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant, fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant,
notary: Party): TransactionBuilder { notary: Party): TransactionBuilder {
val state = State(issuance, issuance.party.owningKey, faceValue, maturityDate) val state = State(issuance, issuance.party, faceValue, maturityDate)
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey)) return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
} }
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) { fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: AbstractParty) {
tx.addInputState(paper) tx.addInputState(paper)
tx.addOutputState(paper.state.data.withOwner(newOwner)) tx.addOutputState(paper.state.data.withOwner(newOwner))
tx.addCommand(Command(Commands.Move(), paper.state.data.owner)) tx.addCommand(Command(Commands.Move(), paper.state.data.owner.owningKey))
} }
@Throws(InsufficientBalanceException::class) @Throws(InsufficientBalanceException::class)
@ -130,6 +130,6 @@ class CommercialPaperLegacy : Contract {
// Add the cash movement using the states in our vault. // Add the cash movement using the states in our vault.
vault.generateSpend(tx, paper.state.data.faceValue.withoutIssuer(), paper.state.data.owner) vault.generateSpend(tx, paper.state.data.faceValue.withoutIssuer(), paper.state.data.owner)
tx.addInputState(paper) tx.addInputState(paper)
tx.addCommand(Command(Commands.Redeem(), paper.state.data.owner)) tx.addCommand(Command(Commands.Redeem(), paper.state.data.owner.owningKey))
} }
} }

View File

@ -20,7 +20,6 @@ import net.corda.core.utilities.Emoji
import net.corda.schemas.CashSchemaV1 import net.corda.schemas.CashSchemaV1
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import java.math.BigInteger import java.math.BigInteger
import java.security.PublicKey
import java.util.* import java.util.*
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -87,27 +86,27 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
override val amount: Amount<Issued<Currency>>, override val amount: Amount<Issued<Currency>>,
/** There must be a MoveCommand signed by this key to claim the amount. */ /** There must be a MoveCommand signed by this key to claim the amount. */
override val owner: PublicKey override val owner: AbstractParty
) : FungibleAsset<Currency>, QueryableState { ) : FungibleAsset<Currency>, QueryableState {
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKey) constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: AbstractParty)
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val exitKeys = setOf(owner, amount.token.issuer.party.owningKey) override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey)
override val contract = CASH_PROGRAM_ID override val contract = CASH_PROGRAM_ID
override val participants = listOf(owner) override val participants = listOf(owner)
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKey): FungibleAsset<Currency> override fun move(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty): FungibleAsset<Currency>
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner) = copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)" override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
/** Object Relational Mapping support. */ /** Object Relational Mapping support. */
override fun generateMappedObject(schema: MappedSchema): PersistentState { override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) { return when (schema) {
is CashSchemaV1 -> CashSchemaV1.PersistentCashState( is CashSchemaV1 -> CashSchemaV1.PersistentCashState(
owner = this.owner.toBase58String(), owner = this.owner.owningKey.toBase58String(),
pennies = this.amount.quantity, pennies = this.amount.quantity,
currency = this.amount.token.product.currencyCode, currency = this.amount.token.product.currencyCode,
issuerParty = this.amount.token.issuer.party.owningKey.toBase58String(), issuerParty = this.amount.token.issuer.party.owningKey.toBase58String(),
@ -148,16 +147,16 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
/** /**
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey. * Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKey, notary: Party) fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: AbstractParty, notary: Party)
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary) = generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
/** /**
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKey, notary: Party) fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: AbstractParty, notary: Party)
= generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand()) = generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand())
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKey) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: AbstractParty)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))
override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount) override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount)
@ -175,7 +174,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
* if there are none, or if any of the cash states cannot be added together (i.e. are * if there are none, or if any of the cash states cannot be added together (i.e. are
* different currencies or issuers). * different currencies or issuers).
*/ */
fun Iterable<ContractState>.sumCashBy(owner: PublicKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow() fun Iterable<ContractState>.sumCashBy(owner: AbstractParty): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
/** /**
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash * Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
@ -191,12 +190,12 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency) return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
} }
fun Cash.State.ownedBy(owner: PublicKey) = copy(owner = owner) fun Cash.State.ownedBy(owner: AbstractParty) = copy(owner = owner)
fun Cash.State.issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party.toAnonymous())))) fun Cash.State.issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party))))
fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit))) fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit)))
fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit))) fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))
infix fun Cash.State.`owned by`(owner: PublicKey) = ownedBy(owner) infix fun Cash.State.`owned by`(owner: AbstractParty) = ownedBy(owner)
infix fun Cash.State.`issued by`(party: AbstractParty) = issuedBy(party) infix fun Cash.State.`issued by`(party: AbstractParty) = issuedBy(party)
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit) infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit) infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)
@ -208,6 +207,6 @@ val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */ /** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
val DUMMY_CASH_ISSUER by lazy { Party(X500Name("CN=Snake Oil Issuer,O=R3,OU=corda,L=London,C=UK"), DUMMY_CASH_ISSUER_KEY.public).ref(1) } val DUMMY_CASH_ISSUER by lazy { Party(X500Name("CN=Snake Oil Issuer,O=R3,OU=corda,L=London,C=UK"), DUMMY_CASH_ISSUER_KEY.public).ref(1) }
/** An extension property that lets you write 100.DOLLARS.CASH */ /** An extension property that lets you write 100.DOLLARS.CASH */
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey) val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NULL_PARTY)
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */ /** An extension property that lets you get a cash state from an issued token, under the [NULL_PARTY] */
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKey) val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NULL_PARTY)

View File

@ -9,10 +9,10 @@ import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.util.* import java.util.*
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -94,21 +94,21 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
override val amount: Amount<Issued<Commodity>>, override val amount: Amount<Issued<Commodity>>,
/** There must be a MoveCommand signed by this key to claim the amount */ /** There must be a MoveCommand signed by this key to claim the amount */
override val owner: PublicKey override val owner: AbstractParty
) : FungibleAsset<Commodity> { ) : FungibleAsset<Commodity> {
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKey) constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: AbstractParty)
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val contract = COMMODITY_PROGRAM_ID override val contract = COMMODITY_PROGRAM_ID
override val exitKeys = Collections.singleton(owner) override val exitKeys = Collections.singleton(owner.owningKey)
override val participants = listOf(owner) override val participants = listOf(owner)
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKey): FungibleAsset<Commodity> override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: AbstractParty): FungibleAsset<Commodity>
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner) = copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)" override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
} }
// Just for grouping // Just for grouping
@ -145,17 +145,17 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
/** /**
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey. * Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKey, notary: Party) fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: AbstractParty, notary: Party)
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary) = generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
/** /**
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKey, notary: Party) fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: AbstractParty, notary: Party)
= generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand()) = generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand())
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKey) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: AbstractParty)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))
override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount) override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount)

View File

@ -5,7 +5,9 @@ import net.corda.contracts.asset.Obligation.Lifecycle.NORMAL
import net.corda.contracts.clause.* import net.corda.contracts.clause.*
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.* import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.* import net.corda.core.crypto.NULL_PARTY
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -22,6 +24,36 @@ import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import kotlin.collections.Collection
import kotlin.collections.Iterable
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.collections.Set
import kotlin.collections.all
import kotlin.collections.asIterable
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.contains
import kotlin.collections.distinct
import kotlin.collections.emptySet
import kotlin.collections.filter
import kotlin.collections.filterIsInstance
import kotlin.collections.first
import kotlin.collections.firstOrNull
import kotlin.collections.forEach
import kotlin.collections.groupBy
import kotlin.collections.isNotEmpty
import kotlin.collections.iterator
import kotlin.collections.listOf
import kotlin.collections.map
import kotlin.collections.none
import kotlin.collections.reduce
import kotlin.collections.set
import kotlin.collections.setOf
import kotlin.collections.single
import kotlin.collections.toSet
import kotlin.collections.union
import kotlin.collections.withIndex
// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode. // Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode.
val OBLIGATION_PROGRAM_ID = Obligation<Currency>() val OBLIGATION_PROGRAM_ID = Obligation<Currency>()
@ -274,27 +306,20 @@ class Obligation<P : Any> : Contract {
data class State<P : Any>( data class State<P : Any>(
var lifecycle: Lifecycle = Lifecycle.NORMAL, var lifecycle: Lifecycle = Lifecycle.NORMAL,
/** Where the debt originates from (obligor) */ /** Where the debt originates from (obligor) */
val obligor: AnonymousParty, val obligor: AbstractParty,
val template: Terms<P>, val template: Terms<P>,
val quantity: Long, val quantity: Long,
/** The public key of the entity the contract pays to */ /** The public key of the entity the contract pays to */
val beneficiary: PublicKey val beneficiary: AbstractParty
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> { ) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
constructor(lifecycle: Lifecycle = Lifecycle.NORMAL,
obligor: Party,
template: Terms<P>,
quantity: Long,
beneficiary: PublicKey)
: this(lifecycle, obligor.toAnonymous(), template, quantity, beneficiary)
override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template)) override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template))
override val contract = OBLIGATION_PROGRAM_ID override val contract = OBLIGATION_PROGRAM_ID
override val exitKeys: Collection<PublicKey> = setOf(beneficiary) override val exitKeys: Collection<PublicKey> = setOf(beneficiary.owningKey)
val dueBefore: Instant = template.dueBefore val dueBefore: Instant = template.dueBefore
override val participants: List<PublicKey> = listOf(obligor.owningKey, beneficiary) override val participants: List<AbstractParty> = listOf(obligor, beneficiary)
override val owner: PublicKey = beneficiary override val owner: AbstractParty = beneficiary
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKey): State<P> override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: AbstractParty): State<P>
= copy(quantity = newAmount.quantity, beneficiary = newOwner) = copy(quantity = newAmount.quantity, beneficiary = newOwner)
override fun toString() = when (lifecycle) { override fun toString() = when (lifecycle) {
@ -305,7 +330,7 @@ class Obligation<P : Any> : Contract {
override val bilateralNetState: BilateralNetState<P> override val bilateralNetState: BilateralNetState<P>
get() { get() {
check(lifecycle == Lifecycle.NORMAL) check(lifecycle == Lifecycle.NORMAL)
return BilateralNetState(setOf(obligor.owningKey, beneficiary), template) return BilateralNetState(setOf(obligor, beneficiary), template)
} }
override val multilateralNetState: MultilateralNetState<P> override val multilateralNetState: MultilateralNetState<P>
get() { get() {
@ -327,7 +352,7 @@ class Obligation<P : Any> : Contract {
} }
} }
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(beneficiary = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(beneficiary = newOwner))
} }
// Just for grouping // Just for grouping
@ -418,7 +443,7 @@ class Obligation<P : Any> : Contract {
} }
} }
} }
val owningPubKeys = inputs.filter { it is State<P> }.map { (it as State<P>).beneficiary }.toSet() val owningPubKeys = inputs.filter { it is State<P> }.map { (it as State<P>).beneficiary.owningKey }.toSet()
val keysThatSigned = setLifecycleCommand.signers.toSet() val keysThatSigned = setLifecycleCommand.signers.toSet()
requireThat { requireThat {
"the owning keys are a subset of the signing keys" using keysThatSigned.containsAll(owningPubKeys) "the owning keys are a subset of the signing keys" using keysThatSigned.containsAll(owningPubKeys)
@ -433,7 +458,7 @@ class Obligation<P : Any> : Contract {
* and same parties involved). * and same parties involved).
*/ */
fun generateCloseOutNetting(tx: TransactionBuilder, fun generateCloseOutNetting(tx: TransactionBuilder,
signer: PublicKey, signer: AbstractParty,
vararg states: State<P>) { vararg states: State<P>) {
val netState = states.firstOrNull()?.bilateralNetState val netState = states.firstOrNull()?.bilateralNetState
@ -447,7 +472,7 @@ class Obligation<P : Any> : Contract {
val out = states.reduce(State<P>::net) val out = states.reduce(State<P>::net)
if (out.quantity > 0L) if (out.quantity > 0L)
tx.addOutputState(out) tx.addOutputState(out)
tx.addCommand(Commands.Net(NetType.PAYMENT), signer) tx.addCommand(Commands.Net(NetType.PAYMENT), signer.owningKey)
} }
/** /**
@ -475,9 +500,9 @@ class Obligation<P : Any> : Contract {
obligor: AbstractParty, obligor: AbstractParty,
issuanceDef: Terms<P>, issuanceDef: Terms<P>,
pennies: Long, pennies: Long,
beneficiary: PublicKey, beneficiary: AbstractParty,
notary: Party) notary: Party)
= OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor.toAnonymous(), issuanceDef, pennies, beneficiary), notary), Commands.Issue()) = OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, pennies, beneficiary), notary), Commands.Issue())
fun generatePaymentNetting(tx: TransactionBuilder, fun generatePaymentNetting(tx: TransactionBuilder,
issued: Issued<Obligation.Terms<P>>, issued: Issued<Obligation.Terms<P>>,
@ -487,8 +512,8 @@ class Obligation<P : Any> : Contract {
"all states are in the normal lifecycle state " using (states.all { it.lifecycle == Lifecycle.NORMAL }) "all states are in the normal lifecycle state " using (states.all { it.lifecycle == Lifecycle.NORMAL })
} }
val groups = states.groupBy { it.multilateralNetState } val groups = states.groupBy { it.multilateralNetState }
val partyLookup = HashMap<PublicKey, AnonymousParty>() val partyLookup = HashMap<PublicKey, AbstractParty>()
val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).toSet() val signers = states.map { it.beneficiary }.union(states.map { it.obligor }).toSet()
// Create a lookup table of the party that each public key represents. // Create a lookup table of the party that each public key represents.
states.map { it.obligor }.forEach { partyLookup.put(it.owningKey, it) } states.map { it.obligor }.forEach { partyLookup.put(it.owningKey, it) }
@ -502,12 +527,12 @@ class Obligation<P : Any> : Contract {
netBalances netBalances
// Convert the balances into obligation state objects // Convert the balances into obligation state objects
.map { entry -> .map { entry ->
State(Lifecycle.NORMAL, partyLookup[entry.key.first]!!, State(Lifecycle.NORMAL, entry.key.first,
netState.template, entry.value.quantity, entry.key.second) netState.template, entry.value.quantity, entry.key.second)
} }
// Add the new states to the TX // Add the new states to the TX
.forEach { tx.addOutputState(it, notary) } .forEach { tx.addOutputState(it, notary) }
tx.addCommand(Commands.Net(NetType.PAYMENT), signers.toList()) tx.addCommand(Commands.Net(NetType.PAYMENT), signers.map { it.owningKey })
} }
} }
@ -533,14 +558,14 @@ class Obligation<P : Any> : Contract {
// Produce a new set of states // Produce a new set of states
val groups = statesAndRefs.groupBy { it.state.data.amount.token } val groups = statesAndRefs.groupBy { it.state.data.amount.token }
for ((_, stateAndRefs) in groups) { for ((_, stateAndRefs) in groups) {
val partiesUsed = ArrayList<PublicKey>() val partiesUsed = ArrayList<AbstractParty>()
stateAndRefs.forEach { stateAndRef -> stateAndRefs.forEach { stateAndRef ->
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle) val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
tx.addInputState(stateAndRef) tx.addInputState(stateAndRef)
tx.addOutputState(outState, notary) tx.addOutputState(outState, notary)
partiesUsed.add(stateAndRef.state.data.beneficiary) partiesUsed.add(stateAndRef.state.data.beneficiary)
} }
tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.distinct()) tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.map { it.owningKey }.distinct())
} }
tx.setTime(issuanceDef.dueBefore, issuanceDef.timeTolerance) tx.setTime(issuanceDef.dueBefore, issuanceDef.timeTolerance)
} }
@ -578,7 +603,7 @@ class Obligation<P : Any> : Contract {
val template: Terms<P> = issuanceDef.product val template: Terms<P> = issuanceDef.product
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product) val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
var obligationRemaining: Amount<P> = obligationTotal var obligationRemaining: Amount<P> = obligationTotal
val assetSigners = HashSet<PublicKey>() val assetSigners = HashSet<AbstractParty>()
statesAndRefs.forEach { tx.addInputState(it) } statesAndRefs.forEach { tx.addInputState(it) }
@ -611,7 +636,7 @@ class Obligation<P : Any> : Contract {
} }
// Add the asset move command and obligation settle // Add the asset move command and obligation settle
tx.addCommand(moveCommand, assetSigners.toList()) tx.addCommand(moveCommand, assetSigners.map { it.owningKey })
tx.addCommand(Commands.Settle(Amount((obligationTotal - obligationRemaining).quantity, issuanceDef)), obligationIssuer.owningKey) tx.addCommand(Commands.Settle(Amount((obligationTotal - obligationRemaining).quantity, issuanceDef)), obligationIssuer.owningKey)
} }
@ -630,11 +655,11 @@ class Obligation<P : Any> : Contract {
* *
* @return a map of obligor/beneficiary pairs to the balance due. * @return a map of obligor/beneficiary pairs to the balance due.
*/ */
fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>> { fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<AbstractParty, AbstractParty>, Amount<Obligation.Terms<P>>> {
val balances = HashMap<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>>() val balances = HashMap<Pair<AbstractParty, AbstractParty>, Amount<Obligation.Terms<P>>>()
states.forEach { state -> states.forEach { state ->
val key = Pair(state.obligor.owningKey, state.beneficiary) val key = Pair(state.obligor, state.beneficiary)
val balance = balances[key] ?: Amount(0L, product) val balance = balances[key] ?: Amount(0L, product)
balances[key] = balance + Amount(state.amount.quantity, state.amount.token.product) balances[key] = balance + Amount(state.amount.quantity, state.amount.token.product)
} }
@ -645,8 +670,8 @@ fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<O
/** /**
* Net off the amounts due between parties. * Net off the amounts due between parties.
*/ */
fun <P : Any> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<Pair<PublicKey, PublicKey>, Amount<P>> { fun <P: AbstractParty, T : Any> netAmountsDue(balances: Map<Pair<P, P>, Amount<T>>): Map<Pair<P, P>, Amount<T>> {
val nettedBalances = HashMap<Pair<PublicKey, PublicKey>, Amount<P>>() val nettedBalances = HashMap<Pair<P, P>, Amount<T>>()
balances.forEach { balance -> balances.forEach { balance ->
val (obligor, beneficiary) = balance.key val (obligor, beneficiary) = balance.key
@ -669,9 +694,11 @@ fun <P : Any> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>
* *
* @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being * @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being
* returned. * returned.
* @param P type of party to operate on.
* @param T token that balances represent
*/ */
fun <P : Any> sumAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<PublicKey, Long> { fun <P: AbstractParty, T : Any> sumAmountsDue(balances: Map<Pair<P, P>, Amount<T>>): Map<P, Long> {
val sum = HashMap<PublicKey, Long>() val sum = HashMap<P, Long>()
// Fill the map with zeroes initially // Fill the map with zeroes initially
balances.keys.forEach { balances.keys.forEach {
@ -712,11 +739,11 @@ fun <P : Any> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<O
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef) = filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
infix fun <T : Any> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore)) infix fun <T : Any> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T : Any> Obligation.State<T>.between(parties: Pair<AbstractParty, PublicKey>) = copy(obligor = parties.first.toAnonymous(), beneficiary = parties.second) infix fun <T : Any> Obligation.State<T>.between(parties: Pair<AbstractParty, AbstractParty>) = copy(obligor = parties.first, beneficiary = parties.second)
infix fun <T : Any> Obligation.State<T>.`owned by`(owner: PublicKey) = copy(beneficiary = owner) infix fun <T : Any> Obligation.State<T>.`owned by`(owner: AbstractParty) = copy(beneficiary = owner)
infix fun <T : Any> Obligation.State<T>.`issued by`(party: AbstractParty) = copy(obligor = party.toAnonymous()) infix fun <T : Any> Obligation.State<T>.`issued by`(party: AbstractParty) = copy(obligor = party)
// For Java users: // For Java users:
@Suppress("unused") fun <T : Any> Obligation.State<T>.ownedBy(owner: PublicKey) = copy(beneficiary = owner) @Suppress("unused") fun <T : Any> Obligation.State<T>.ownedBy(owner: AbstractParty) = copy(beneficiary = owner)
@Suppress("unused") fun <T : Any> Obligation.State<T>.issuedBy(party: AnonymousParty) = copy(obligor = party) @Suppress("unused") fun <T : Any> Obligation.State<T>.issuedBy(party: AnonymousParty) = copy(obligor = party)
@ -728,4 +755,4 @@ val DUMMY_OBLIGATION_ISSUER by lazy { Party(X500Name("CN=Snake Oil Issuer,O=R3,O
val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency> val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME) get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency> val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER.toAnonymous(), token.OBLIGATION_DEF, quantity, NullPublicKey) get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NULL_PARTY)

View File

@ -1,7 +1,7 @@
package net.corda.contracts.asset package net.corda.contracts.asset
import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
@ -51,9 +51,9 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
@JvmStatic @JvmStatic
fun <S : FungibleAsset<T>, T: Any> generateSpend(tx: TransactionBuilder, fun <S : FungibleAsset<T>, T: Any> generateSpend(tx: TransactionBuilder,
amount: Amount<T>, amount: Amount<T>,
to: PublicKey, to: AbstractParty,
acceptableStates: List<StateAndRef<S>>, acceptableStates: List<StateAndRef<S>>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>, deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
generateMoveCommand: () -> CommandData): Pair<TransactionBuilder, List<PublicKey>> { generateMoveCommand: () -> CommandData): Pair<TransactionBuilder, List<PublicKey>> {
// Discussion // Discussion
// //
@ -90,7 +90,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
} else { } else {
null null
} }
val keysUsed = gathered.map { it.state.data.owner } val keysUsed = gathered.map { it.state.data.owner.owningKey }
val states = gathered.groupBy { it.state.data.amount.token.issuer }.map { val states = gathered.groupBy { it.state.data.amount.token.issuer }.map {
val coins = it.value val coins = it.value
@ -165,7 +165,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
@JvmStatic @JvmStatic
fun <S : FungibleAsset<T>, T: Any> generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>, fun <S : FungibleAsset<T>, T: Any> generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>, assetStates: List<StateAndRef<S>>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>, deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
generateMoveCommand: () -> CommandData, generateMoveCommand: () -> CommandData,
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey { generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey {
val owner = assetStates.map { it.state.data.owner }.toSet().singleOrNull() ?: throw InsufficientBalanceException(amountIssued) val owner = assetStates.map { it.state.data.owner }.toSet().singleOrNull() ?: throw InsufficientBalanceException(amountIssued)
@ -193,7 +193,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
for (state in gathered) tx.addInputState(state) for (state in gathered) tx.addInputState(state)
for (state in outputs) tx.addOutputState(state) for (state in outputs) tx.addOutputState(state)
tx.addCommand(generateMoveCommand(), gathered.map { it.state.data.owner }) tx.addCommand(generateMoveCommand(), gathered.map { it.state.data.owner.owningKey })
tx.addCommand(generateExitCommand(amountIssued), gathered.flatMap { it.state.data.exitKeys }) tx.addCommand(generateExitCommand(amountIssued), gathered.flatMap { it.state.data.exitKeys })
return amountIssued.token.issuer.party.owningKey return amountIssued.token.issuer.party.owningKey
} }
@ -250,5 +250,5 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
* implementations to have fields in their state which we don't know about here, and we simply leave them untouched * implementations to have fields in their state which we don't know about here, and we simply leave them untouched
* when sending out "change" from spending/exiting. * when sending out "change" from spending/exiting.
*/ */
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKey): TransactionState<S> abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: AbstractParty): TransactionState<S>
} }

View File

@ -3,6 +3,7 @@ package net.corda.contracts.clause
import net.corda.contracts.asset.OnLedgerAsset import net.corda.contracts.asset.OnLedgerAsset
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import java.security.PublicKey import java.security.PublicKey
@ -31,7 +32,7 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
@Throws(InsufficientBalanceException::class) @Throws(InsufficientBalanceException::class)
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>, fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>, assetStates: List<StateAndRef<S>>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>, deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
generateMoveCommand: () -> CommandData, generateMoveCommand: () -> CommandData,
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey
= OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand) = OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand)

View File

@ -6,6 +6,7 @@ import net.corda.contracts.asset.extractAmountsDue
import net.corda.contracts.asset.sumAmountsDue import net.corda.contracts.asset.sumAmountsDue
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.identity.AbstractParty
import java.security.PublicKey import java.security.PublicKey
/** /**
@ -22,7 +23,7 @@ interface NetState<P : Any> {
* Bilateral states are used in close-out netting. * Bilateral states are used in close-out netting.
*/ */
data class BilateralNetState<P : Any>( data class BilateralNetState<P : Any>(
val partyKeys: Set<PublicKey>, val partyKeys: Set<AbstractParty>,
override val template: Obligation.Terms<P> override val template: Obligation.Terms<P>
) : NetState<P> ) : NetState<P>
@ -86,7 +87,7 @@ open class NetClause<C : CommandData, P : Any> : Clause<ContractState, C, Unit>(
} }
// TODO: Handle proxies nominated by parties, i.e. a central clearing service // TODO: Handle proxies nominated by parties, i.e. a central clearing service
val involvedParties = inputs.map { it.beneficiary }.union(inputs.map { it.obligor.owningKey }).toSet() val involvedParties: Set<PublicKey> = inputs.map { it.beneficiary.owningKey }.union(inputs.map { it.obligor.owningKey }).toSet()
when (command.value.type) { when (command.value.type) {
// For close-out netting, allow any involved party to sign // For close-out netting, allow any involved party to sign
NetType.CLOSE_OUT -> require(command.signers.intersect(involvedParties).isNotEmpty()) { "any involved party has signed" } NetType.CLOSE_OUT -> require(command.signers.intersect(involvedParties).isNotEmpty()) { "any involved party has signed" }

View File

@ -5,6 +5,7 @@ import net.corda.core.contracts.DealState
import net.corda.core.contracts.TransactionForContract import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -17,12 +18,12 @@ class DummyDealContract : Contract {
data class State( data class State(
override val contract: Contract = DummyDealContract(), override val contract: Contract = DummyDealContract(),
override val participants: List<PublicKey> = listOf(), override val participants: List<AbstractParty> = listOf(),
override val linearId: UniqueIdentifier = UniqueIdentifier(), override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val ref: String, override val ref: String,
override val parties: List<AnonymousParty> = listOf()) : DealState { override val parties: List<AnonymousParty> = listOf()) : DealState {
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { it.containsAny(ourKeys) } return participants.any { it.owningKey.containsAny(ourKeys) }
} }
override fun generateAgreement(notary: Party): TransactionBuilder { override fun generateAgreement(notary: Party): TransactionBuilder {

View File

@ -4,9 +4,9 @@ import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.contracts.clauses.FilterOn import net.corda.core.contracts.clauses.FilterOn
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny import net.corda.core.crypto.containsAny
import net.corda.core.identity.AbstractParty
import java.security.PublicKey import java.security.PublicKey
class DummyLinearContract : Contract { class DummyLinearContract : Contract {
@ -20,11 +20,11 @@ class DummyLinearContract : Contract {
data class State( data class State(
override val linearId: UniqueIdentifier = UniqueIdentifier(), override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val contract: Contract = DummyLinearContract(), override val contract: Contract = DummyLinearContract(),
override val participants: List<PublicKey> = listOf(), override val participants: List<AbstractParty> = listOf(),
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState { val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { it.containsAny(ourKeys) } return participants.any { it.owningKey.containsAny(ourKeys) }
} }
} }
} }

View File

@ -6,7 +6,8 @@ import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.DUMMY_CASH_ISSUER import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
@ -21,13 +22,15 @@ import java.util.*
@JvmOverloads @JvmOverloads
fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>, fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
revisions: Int? = 0, revisions: Int? = 0,
participants: List<PublicKey> = emptyList()) : Vault<DealState> { participants: List<AbstractParty> = emptyList()) : Vault<DealState> {
val freshKey = keyManagementService.freshKey() val freshKey = keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val recipient = AnonymousParty(freshPublicKey)
val transactions: List<SignedTransaction> = dealIds.map { val transactions: List<SignedTransaction> = dealIds.map {
// Issue a deal state // Issue a deal state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(freshKey.public))) addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(recipient)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
} }
@ -47,13 +50,15 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
@JvmOverloads @JvmOverloads
fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int, fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
uid: UniqueIdentifier = UniqueIdentifier(), uid: UniqueIdentifier = UniqueIdentifier(),
participants: List<PublicKey> = emptyList()) : Vault<LinearState> { participants: List<AbstractParty> = emptyList()) : Vault<LinearState> {
val freshKey = keyManagementService.freshKey() val freshKey = keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val recipient = AnonymousParty(freshPublicKey)
val transactions: List<SignedTransaction> = (1..numberToCreate).map { val transactions: List<SignedTransaction> = (1..numberToCreate).map {
// Issue a Linear state // Issue a Linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = uid, participants = participants.plus(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = uid, participants = participants.plus(recipient)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
} }
@ -87,18 +92,19 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
atMostThisManyStates: Int = 10, atMostThisManyStates: Int = 10,
rng: Random = Random(), rng: Random = Random(),
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })), ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
ownedBy: PublicKey? = null, ownedBy: AbstractParty? = null,
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER, issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault<Cash.State> { issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault<Cash.State> {
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng) val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
val myKey: PublicKey = ownedBy ?: myInfo.legalIdentity.owningKey val myKey: PublicKey = ownedBy?.owningKey ?: myInfo.legalIdentity.owningKey
val me = AnonymousParty(myKey)
// We will allocate one state to one transaction, for simplicities sake. // We will allocate one state to one transaction, for simplicities sake.
val cash = Cash() val cash = Cash()
val transactions: List<SignedTransaction> = amounts.map { pennies -> val transactions: List<SignedTransaction> = amounts.map { pennies ->
val issuance = TransactionType.General.Builder(null) val issuance = TransactionType.General.Builder(null)
cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy.copy(reference = ref), howMuch.token)), myKey, outputNotary) cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy.copy(reference = ref), howMuch.token)), me, outputNotary)
issuance.signWith(issuerKey) issuance.signWith(issuerKey)
return@map issuance.toSignedTransaction(true) return@map issuance.toSignedTransaction(true)

View File

@ -60,7 +60,7 @@ class CashExitFlow(val amount: Amount<Currency>, val issueRef: OpaqueBytes, prog
// count as a reason to fail? // count as a reason to fail?
val participants: Set<Party> = inputStates val participants: Set<Party> = inputStates
.filterIsInstance<Cash.State>() .filterIsInstance<Cash.State>()
.map { serviceHub.identityService.partyFromKey(it.owner) } .map { serviceHub.identityService.partyFromAnonymous(it.owner) }
.filterNotNull() .filterNotNull()
.toSet() .toSet()

View File

@ -37,7 +37,8 @@ class CashIssueFlow(val amount: Amount<Currency>,
progressTracker.currentStep = GENERATING_TX progressTracker.currentStep = GENERATING_TX
val builder: TransactionBuilder = TransactionType.General.Builder(notary = null) val builder: TransactionBuilder = TransactionType.General.Builder(notary = null)
val issuer = serviceHub.myInfo.legalIdentity.ref(issueRef) val issuer = serviceHub.myInfo.legalIdentity.ref(issueRef)
Cash().generateIssue(builder, amount.issuedBy(issuer), recipient.owningKey, notary) // TODO: Get a transaction key, don't just re-use the owning key
Cash().generateIssue(builder, amount.issuedBy(issuer), recipient, notary)
progressTracker.currentStep = SIGNING_TX progressTracker.currentStep = SIGNING_TX
val myKey = serviceHub.legalIdentityKey val myKey = serviceHub.legalIdentityKey
builder.signWith(myKey) builder.signWith(myKey)

View File

@ -39,7 +39,8 @@ open class CashPaymentFlow(
serviceHub.vaultService.generateSpend( serviceHub.vaultService.generateSpend(
builder, builder,
amount, amount,
recipient.owningKey, // TODO: Get a transaction key, don't just re-use the owning key
recipient,
issuerConstraint) issuerConstraint)
} catch (e: InsufficientBalanceException) { } catch (e: InsufficientBalanceException) {
throw CashException("Insufficient cash for spend: ${e.message}", e) throw CashException("Insufficient cash for spend: ${e.message}", e)

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.expandedCompositeKeys
import net.corda.core.crypto.sign import net.corda.core.crypto.sign
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.seconds import net.corda.core.seconds
@ -34,7 +35,7 @@ import java.util.*
* 3. S signs it and commits it to the ledger, notarising it and distributing the final signed transaction back * 3. S signs it and commits it to the ledger, notarising it and distributing the final signed transaction back
* to B. * to B.
* *
* Assuming no malicious termination, they both end the flow being in posession of a valid, signed transaction * Assuming no malicious termination, they both end the flow being in possession of a valid, signed transaction
* that represents an atomic asset swap. * that represents an atomic asset swap.
* *
* Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine. * Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
@ -118,7 +119,7 @@ object TwoPartyTradeFlow {
// even though it is missing signatures. // even though it is missing signatures.
subFlow(ResolveTransactionsFlow(wtx, otherParty)) subFlow(ResolveTransactionsFlow(wtx, otherParty))
if (wtx.outputs.map { it.data }.sumCashBy(myPublicKey).withoutIssuer() != price) if (wtx.outputs.map { it.data }.sumCashBy(AnonymousParty(myPublicKey)).withoutIssuer() != price)
throw FlowException("Transaction is not sending us the right amount of cash") throw FlowException("Transaction is not sending us the right amount of cash")
it it
@ -219,7 +220,7 @@ object TwoPartyTradeFlow {
val ptx = TransactionType.General.Builder(notary) val ptx = TransactionType.General.Builder(notary)
// Add input and output states for the movement of cash, by using the Cash contract to generate the states // Add input and output states for the movement of cash, by using the Cash contract to generate the states
val (tx, cashSigningPubKeys) = serviceHub.vaultService.generateSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey) val (tx, cashSigningPubKeys) = serviceHub.vaultService.generateSpend(ptx, tradeRequest.price, AnonymousParty(tradeRequest.sellerOwnerKey))
// Add inputs/outputs/a command for the movement of the asset. // Add inputs/outputs/a command for the movement of the asset.
tx.addInputState(tradeRequest.assetForSale) tx.addInputState(tradeRequest.assetForSale)
@ -228,10 +229,10 @@ object TwoPartyTradeFlow {
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to // we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
// reveal who the owner actually is. The key management service is expected to derive a unique key from some // reveal who the owner actually is. The key management service is expected to derive a unique key from some
// initial seed in order to provide privacy protection. // initial seed in order to provide privacy protection.
val freshKey = serviceHub.keyManagementService.freshKey() val freshPublicKey = serviceHub.keyManagementService.freshKey().public
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public) val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(AnonymousParty(freshPublicKey))
tx.addOutputState(state, tradeRequest.assetForSale.state.notary) tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner) tx.addCommand(command, tradeRequest.assetForSale.state.data.owner.owningKey)
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt // And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
// to have one. // to have one.

View File

@ -1,12 +1,15 @@
package net.corda.contracts.asset; package net.corda.contracts.asset;
import kotlin.*; import kotlin.Unit;
import net.corda.core.contracts.*; import net.corda.core.contracts.PartyAndReference;
import net.corda.core.serialization.*; import net.corda.core.identity.AnonymousParty;
import org.junit.*; import net.corda.core.serialization.OpaqueBytes;
import org.junit.Test;
import static net.corda.core.contracts.ContractsDSL.*; import static net.corda.core.contracts.ContractsDSL.DOLLARS;
import static net.corda.core.utilities.TestConstants.*; import static net.corda.core.contracts.ContractsDSL.issuedBy;
import static net.corda.core.utilities.TestConstants.getDUMMY_PUBKEY_1;
import static net.corda.core.utilities.TestConstants.getDUMMY_PUBKEY_2;
import static net.corda.testing.CoreTestUtils.*; import static net.corda.testing.CoreTestUtils.*;
/** /**
@ -15,8 +18,8 @@ import static net.corda.testing.CoreTestUtils.*;
public class CashTestsJava { public class CashTestsJava {
private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{1}); private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{1});
private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef); private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef);
private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), getDUMMY_PUBKEY_1()); private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(getDUMMY_PUBKEY_1()));
private final Cash.State outState = new Cash.State(inState.getAmount(), getDUMMY_PUBKEY_2()); private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(getDUMMY_PUBKEY_2()));
@Test @Test
public void trivial() { public void trivial() {
@ -26,7 +29,7 @@ public class CashTestsJava {
tx.failsWith("the amounts balance"); tx.failsWith("the amounts balance");
tx.tweak(tw -> { tx.tweak(tw -> {
tw.output(new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), getDUMMY_PUBKEY_2())); tw.output(new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getDUMMY_PUBKEY_2())));
return tw.failsWith("the amounts balance"); return tw.failsWith("the amounts balance");
}); });

View File

@ -4,15 +4,13 @@ import net.corda.contracts.asset.*
import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.days import net.corda.core.days
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.*
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.core.utilities.TEST_TX_TIME
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.* import net.corda.testing.*
@ -38,7 +36,7 @@ interface ICommercialPaperTestTemplate {
class JavaCommercialPaperTest : ICommercialPaperTestTemplate { class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State( override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State(
MEGA_CORP.ref(123), MEGA_CORP.ref(123),
MEGA_CORP_PUBKEY, MEGA_CORP,
1000.DOLLARS `issued by` MEGA_CORP.ref(123), 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
TEST_TX_TIME + 7.days TEST_TX_TIME + 7.days
) )
@ -51,7 +49,7 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
class KotlinCommercialPaperTest : ICommercialPaperTestTemplate { class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = CommercialPaper.State( override fun getPaper(): ICommercialPaperState = CommercialPaper.State(
issuance = MEGA_CORP.ref(123), issuance = MEGA_CORP.ref(123),
owner = MEGA_CORP_PUBKEY, owner = MEGA_CORP,
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123), faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
maturityDate = TEST_TX_TIME + 7.days maturityDate = TEST_TX_TIME + 7.days
) )
@ -64,7 +62,7 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate { class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = CommercialPaperLegacy.State( override fun getPaper(): ICommercialPaperState = CommercialPaperLegacy.State(
issuance = MEGA_CORP.ref(123), issuance = MEGA_CORP.ref(123),
owner = MEGA_CORP_PUBKEY, owner = MEGA_CORP,
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123), faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
maturityDate = TEST_TX_TIME + 7.days maturityDate = TEST_TX_TIME + 7.days
) )
@ -91,8 +89,8 @@ class CommercialPaperTestsGeneric {
val someProfits = 1200.DOLLARS `issued by` issuer val someProfits = 1200.DOLLARS `issued by` issuer
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY) output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE)
output("some profits", someProfits.STATE `owned by` MEGA_CORP_PUBKEY) output("some profits", someProfits.STATE `owned by` MEGA_CORP)
} }
// Some CP is issued onto the ledger by MegaCorp. // Some CP is issued onto the ledger by MegaCorp.
@ -108,8 +106,8 @@ class CommercialPaperTestsGeneric {
transaction("Trade") { transaction("Trade") {
input("paper") input("paper")
input("alice's $900") input("alice's $900")
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP }
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE_PUBKEY } output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE }
command(ALICE_PUBKEY) { Cash.Commands.Move() } command(ALICE_PUBKEY) { Cash.Commands.Move() }
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() } command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
this.verifies() this.verifies()
@ -122,8 +120,8 @@ class CommercialPaperTestsGeneric {
input("some profits") input("some profits")
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) { fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY } output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE }
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY } output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP }
} }
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
@ -270,8 +268,8 @@ class CommercialPaperTestsGeneric {
// Alice pays $9000 to BigCorp to own some of their debt. // Alice pays $9000 to BigCorp to own some of their debt.
moveTX = run { moveTX = run {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY) val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public) aliceVaultService.generateSpend(ptx, 9000.DOLLARS, AnonymousParty(bigCorpServices.key.public))
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public) CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), AnonymousParty(aliceServices.key.public))
ptx.signWith(bigCorpServices.key) ptx.signWith(bigCorpServices.key)
ptx.signWith(aliceServices.key) ptx.signWith(aliceServices.key)
ptx.signWith(DUMMY_NOTARY_KEY) ptx.signWith(DUMMY_NOTARY_KEY)

View File

@ -2,8 +2,10 @@ package net.corda.contracts.asset
import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.node.services.unconsumedStates import net.corda.core.node.services.unconsumedStates
@ -23,7 +25,6 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.Closeable import java.io.Closeable
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
import kotlin.test.* import kotlin.test.*
@ -32,11 +33,11 @@ class CashTests {
val defaultIssuer = MEGA_CORP.ref(defaultRef) val defaultIssuer = MEGA_CORP.ref(defaultRef)
val inState = Cash.State( val inState = Cash.State(
amount = 1000.DOLLARS `issued by` defaultIssuer, amount = 1000.DOLLARS `issued by` defaultIssuer,
owner = DUMMY_PUBKEY_1 owner = AnonymousParty(DUMMY_PUBKEY_1)
) )
// Input state held by the issuer // Input state held by the issuer
val issuerInState = inState.copy(owner = defaultIssuer.party.owningKey) val issuerInState = inState.copy(owner = defaultIssuer.party)
val outState = issuerInState.copy(owner = DUMMY_PUBKEY_2) val outState = issuerInState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2))
fun Cash.State.editDepositRef(ref: Byte) = copy( fun Cash.State.editDepositRef(ref: Byte) = copy(
amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref)))) amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref))))
@ -70,13 +71,13 @@ class CashTests {
} }
services.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, services.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_PUBKEY_1) issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1)
services.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, services.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_PUBKEY_1) issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1)
services.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1, services.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_PUBKEY_1) issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1)
services.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1, services.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_PUBKEY_1) issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1)
vaultStatesUnconsumed = services.vaultService.unconsumedStates<Cash.State>().toList() vaultStatesUnconsumed = services.vaultService.unconsumedStates<Cash.State>().toList()
} }
@ -142,7 +143,7 @@ class CashTests {
output { output {
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34), amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
owner = DUMMY_PUBKEY_1 owner = AnonymousParty(DUMMY_PUBKEY_1)
) )
} }
tweak { tweak {
@ -158,14 +159,14 @@ class CashTests {
fun generateIssueRaw() { fun generateIssueRaw() {
// Test generation works. // Test generation works.
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply { val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx }.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty()) assertTrue(tx.inputs.isEmpty())
val s = tx.outputs[0].data as Cash.State val s = tx.outputs[0].data as Cash.State
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount) assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party) assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
assertEquals(DUMMY_PUBKEY_1, s.owner) assertEquals(AnonymousParty(DUMMY_PUBKEY_1), s.owner)
assertTrue(tx.commands[0].value is Cash.Commands.Issue) assertTrue(tx.commands[0].value is Cash.Commands.Issue)
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0]) assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
} }
@ -175,7 +176,7 @@ class CashTests {
// Test issuance from an issued amount // Test issuance from an issued amount
val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34) val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34)
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply { val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
Cash().generateIssue(this, amount, owner = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY) Cash().generateIssue(this, amount, owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx }.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty()) assertTrue(tx.inputs.isEmpty())
@ -248,14 +249,14 @@ class CashTests {
// Issue some cash // Issue some cash
var ptx = TransactionType.General.Builder(DUMMY_NOTARY) var ptx = TransactionType.General.Builder(DUMMY_NOTARY)
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY) Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY)
ptx.signWith(MINI_CORP_KEY) ptx.signWith(MINI_CORP_KEY)
val tx = ptx.toSignedTransaction() val tx = ptx.toSignedTransaction()
// Include the previously issued cash in a new issuance command // Include the previously issued cash in a new issuance command
ptx = TransactionType.General.Builder(DUMMY_NOTARY) ptx = TransactionType.General.Builder(DUMMY_NOTARY)
ptx.addInputState(tx.tx.outRef<Cash.State>(0)) ptx.addInputState(tx.tx.outRef<Cash.State>(0))
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY) Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY)
} }
@Test @Test
@ -329,7 +330,7 @@ class CashTests {
input { input {
inState.copy( inState.copy(
amount = 150.POUNDS `issued by` defaultIssuer, amount = 150.POUNDS `issued by` defaultIssuer,
owner = DUMMY_PUBKEY_2 owner = AnonymousParty(DUMMY_PUBKEY_2)
) )
} }
output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) } output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
@ -382,10 +383,10 @@ class CashTests {
// Multi-issuer case. // Multi-issuer case.
transaction { transaction {
input { issuerInState } input { issuerInState }
input { issuerInState.copy(owner = MINI_CORP_PUBKEY) `issued by` MINI_CORP } input { issuerInState.copy(owner = MINI_CORP) `issued by` MINI_CORP }
output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP } output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP }
output { issuerInState.copy(owner = MINI_CORP_PUBKEY, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) } output { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() }
@ -420,19 +421,19 @@ class CashTests {
// Can't merge them together. // Can't merge them together.
tweak { tweak {
output { inState.copy(owner = DUMMY_PUBKEY_2, amount = 2000.DOLLARS `issued by` defaultIssuer) } output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2), amount = 2000.DOLLARS `issued by` defaultIssuer) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Missing MiniCorp deposit // Missing MiniCorp deposit
tweak { tweak {
output { inState.copy(owner = DUMMY_PUBKEY_2) } output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(owner = DUMMY_PUBKEY_2) } output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// This works. // This works.
output { inState.copy(owner = DUMMY_PUBKEY_2) } output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(owner = DUMMY_PUBKEY_2) `issued by` MINI_CORP } output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) `issued by` MINI_CORP }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() } command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
@ -442,11 +443,11 @@ class CashTests {
fun multiCurrency() { fun multiCurrency() {
// Check we can do an atomic currency trade tx. // Check we can do an atomic currency trade tx.
transaction { transaction {
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), DUMMY_PUBKEY_2) val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(DUMMY_PUBKEY_2))
input { inState `owned by` DUMMY_PUBKEY_1 } input { inState `owned by` AnonymousParty(DUMMY_PUBKEY_1) }
input { pounds } input { pounds }
output { inState `owned by` DUMMY_PUBKEY_2 } output { inState `owned by` AnonymousParty(DUMMY_PUBKEY_2) }
output { pounds `owned by` DUMMY_PUBKEY_1 } output { pounds `owned by` AnonymousParty(DUMMY_PUBKEY_1) }
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Cash.Commands.Move() } command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Cash.Commands.Move() }
this.verifies() this.verifies()
@ -458,13 +459,13 @@ class CashTests {
// Spend tx generation // Spend tx generation
val OUR_KEY: KeyPair by lazy { generateKeyPair() } val OUR_KEY: KeyPair by lazy { generateKeyPair() }
val OUR_PUBKEY_1: PublicKey get() = OUR_KEY.public val OUR_IDENTITY_1: AbstractParty get() = AnonymousParty(OUR_KEY.public)
val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2 val THEIR_IDENTITY_1 = AnonymousParty(DUMMY_PUBKEY_2)
fun makeCash(amount: Amount<Currency>, corp: Party, depositRef: Byte = 1) = fun makeCash(amount: Amount<Currency>, corp: Party, depositRef: Byte = 1) =
StateAndRef( StateAndRef(
Cash.State(amount `issued by` corp.ref(depositRef), OUR_PUBKEY_1) `with notary` DUMMY_NOTARY, Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1) `with notary` DUMMY_NOTARY,
StateRef(SecureHash.randomSHA256(), Random().nextInt(32)) StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
) )
@ -484,7 +485,7 @@ class CashTests {
return tx.toWireTransaction() return tx.toWireTransaction()
} }
fun makeSpend(amount: Amount<Currency>, dest: PublicKey): WireTransaction { fun makeSpend(amount: Amount<Currency>, dest: AbstractParty): WireTransaction {
val tx = TransactionType.General.Builder(DUMMY_NOTARY) val tx = TransactionType.General.Builder(DUMMY_NOTARY)
database.transaction { database.transaction {
vault.generateSpend(tx, amount, dest) vault.generateSpend(tx, amount, dest)
@ -566,13 +567,13 @@ class CashTests {
database.transaction { database.transaction {
val wtx = makeSpend(100.DOLLARS, THEIR_PUBKEY_1) val wtx = makeSpend(100.DOLLARS, THEIR_IDENTITY_1)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val vaultState = vaultStatesUnconsumed.elementAt(0) val vaultState = vaultStatesUnconsumed.elementAt(0)
assertEquals(vaultState.ref, wtx.inputs[0]) assertEquals(vaultState.ref, wtx.inputs[0])
assertEquals(vaultState.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[0].data) assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1), wtx.outputs[0].data)
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0]) assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
} }
} }
@ -582,7 +583,7 @@ class CashTests {
database.transaction { database.transaction {
val tx = TransactionType.General.Builder(DUMMY_NOTARY) val tx = TransactionType.General.Builder(DUMMY_NOTARY)
vault.generateSpend(tx, 80.DOLLARS, ALICE_PUBKEY, setOf(MINI_CORP.toAnonymous())) vault.generateSpend(tx, 80.DOLLARS, ALICE, setOf(MINI_CORP))
assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0]) assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0])
} }
@ -593,14 +594,14 @@ class CashTests {
database.transaction { database.transaction {
val wtx = makeSpend(10.DOLLARS, THEIR_PUBKEY_1) val wtx = makeSpend(10.DOLLARS, THEIR_IDENTITY_1)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val vaultState = vaultStatesUnconsumed.elementAt(0) val vaultState = vaultStatesUnconsumed.elementAt(0)
assertEquals(vaultState.ref, wtx.inputs[0]) assertEquals(vaultState.ref, wtx.inputs[0])
assertEquals(vaultState.state.data.copy(owner = THEIR_PUBKEY_1, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data) assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
assertEquals(vaultState.state.data.copy(amount = 90.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data) assertEquals(vaultState.state.data.copy(amount = 90.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0]) assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
} }
} }
@ -608,15 +609,15 @@ class CashTests {
fun generateSpendWithTwoInputs() { fun generateSpendWithTwoInputs() {
database.transaction { database.transaction {
val wtx = makeSpend(500.DOLLARS, THEIR_PUBKEY_1) val wtx = makeSpend(500.DOLLARS, THEIR_IDENTITY_1)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val vaultState0 = vaultStatesUnconsumed.elementAt(0) val vaultState0 = vaultStatesUnconsumed.elementAt(0)
val vaultState1 = vaultStatesUnconsumed.elementAt(1) val vaultState1 = vaultStatesUnconsumed.elementAt(1)
assertEquals(vaultState0.ref, wtx.inputs[0]) assertEquals(vaultState0.ref, wtx.inputs[0])
assertEquals(vaultState1.ref, wtx.inputs[1]) assertEquals(vaultState1.ref, wtx.inputs[1])
assertEquals(vaultState0.state.data.copy(owner = THEIR_PUBKEY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data) assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0]) assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
} }
} }
@ -624,7 +625,7 @@ class CashTests {
fun generateSpendMixedDeposits() { fun generateSpendMixedDeposits() {
database.transaction { database.transaction {
val wtx = makeSpend(580.DOLLARS, THEIR_PUBKEY_1) val wtx = makeSpend(580.DOLLARS, THEIR_IDENTITY_1)
assertEquals(3, wtx.inputs.size) assertEquals(3, wtx.inputs.size)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@ -635,9 +636,9 @@ class CashTests {
assertEquals(vaultState0.ref, wtx.inputs[0]) assertEquals(vaultState0.ref, wtx.inputs[0])
assertEquals(vaultState1.ref, wtx.inputs[1]) assertEquals(vaultState1.ref, wtx.inputs[1])
assertEquals(vaultState2.ref, wtx.inputs[2]) assertEquals(vaultState2.ref, wtx.inputs[2])
assertEquals(vaultState0.state.data.copy(owner = THEIR_PUBKEY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data) assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
assertEquals(vaultState2.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[0].data) assertEquals(vaultState2.state.data.copy(owner = THEIR_IDENTITY_1), wtx.outputs[0].data)
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0]) assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
} }
} }
@ -647,12 +648,12 @@ class CashTests {
database.transaction { database.transaction {
val e: InsufficientBalanceException = assertFailsWith("balance") { val e: InsufficientBalanceException = assertFailsWith("balance") {
makeSpend(1000.DOLLARS, THEIR_PUBKEY_1) makeSpend(1000.DOLLARS, THEIR_IDENTITY_1)
} }
assertEquals((1000 - 580).DOLLARS, e.amountMissing) assertEquals((1000 - 580).DOLLARS, e.amountMissing)
assertFailsWith(InsufficientBalanceException::class) { assertFailsWith(InsufficientBalanceException::class) {
makeSpend(81.SWISS_FRANCS, THEIR_PUBKEY_1) makeSpend(81.SWISS_FRANCS, THEIR_IDENTITY_1)
} }
} }
} }
@ -662,9 +663,9 @@ class CashTests {
*/ */
@Test @Test
fun aggregation() { fun aggregation() {
val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` MEGA_CORP.ref(2), MEGA_CORP_PUBKEY) val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` MEGA_CORP.ref(2), MEGA_CORP)
val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` MEGA_CORP.ref(2), MINI_CORP_PUBKEY) val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` MEGA_CORP.ref(2), MINI_CORP)
val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), MEGA_CORP_PUBKEY) val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), MEGA_CORP)
// Obviously it must be possible to aggregate states with themselves // Obviously it must be possible to aggregate states with themselves
assertEquals(fiveThousandDollarsFromMega.amount.token, fiveThousandDollarsFromMega.amount.token) assertEquals(fiveThousandDollarsFromMega.amount.token, fiveThousandDollarsFromMega.amount.token)
@ -678,7 +679,7 @@ class CashTests {
// States cannot be aggregated if the currency differs // States cannot be aggregated if the currency differs
assertNotEquals(oneThousandDollarsFromMini.amount.token, assertNotEquals(oneThousandDollarsFromMini.amount.token,
Cash.State(1000.POUNDS `issued by` MINI_CORP.ref(3), MEGA_CORP_PUBKEY).amount.token) Cash.State(1000.POUNDS `issued by` MINI_CORP.ref(3), MEGA_CORP).amount.token)
// States cannot be aggregated if the reference differs // States cannot be aggregated if the reference differs
assertNotEquals(fiveThousandDollarsFromMega.amount.token, (fiveThousandDollarsFromMega `with deposit` defaultIssuer).amount.token) assertNotEquals(fiveThousandDollarsFromMega.amount.token, (fiveThousandDollarsFromMega `with deposit` defaultIssuer).amount.token)
@ -688,20 +689,20 @@ class CashTests {
@Test @Test
fun `summing by owner`() { fun `summing by owner`() {
val states = listOf( val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP_PUBKEY), Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY), Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY) Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
) )
assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(MEGA_CORP_PUBKEY)) assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(MEGA_CORP))
} }
@Test(expected = UnsupportedOperationException::class) @Test(expected = UnsupportedOperationException::class)
fun `summing by owner throws`() { fun `summing by owner throws`() {
val states = listOf( val states = listOf(
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY), Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY) Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
) )
states.sumCashBy(MINI_CORP_PUBKEY) states.sumCashBy(MINI_CORP)
} }
@Test @Test
@ -720,9 +721,9 @@ class CashTests {
@Test @Test
fun `summing a single currency`() { fun `summing a single currency`() {
val states = listOf( val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY), Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY), Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY) Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
) )
// Test that summing everything produces the total number of dollars // Test that summing everything produces the total number of dollars
val expected = 7000.DOLLARS `issued by` defaultIssuer val expected = 7000.DOLLARS `issued by` defaultIssuer
@ -733,8 +734,8 @@ class CashTests {
@Test(expected = IllegalArgumentException::class) @Test(expected = IllegalArgumentException::class)
fun `summing multiple currencies`() { fun `summing multiple currencies`() {
val states = listOf( val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY), Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP_PUBKEY) Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP)
) )
// Test that summing everything fails because we're mixing units // Test that summing everything fails because we're mixing units
states.sumCash() states.sumCash()
@ -748,14 +749,14 @@ class CashTests {
output("MEGA_CORP cash") { output("MEGA_CORP cash") {
Cash.State( Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1), amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP_PUBKEY owner = MEGA_CORP
) )
} }
} }
transaction { transaction {
input("MEGA_CORP cash") input("MEGA_CORP cash")
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1)) output("MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(DUMMY_PUBKEY_1)))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
@ -764,7 +765,7 @@ class CashTests {
transaction { transaction {
input("MEGA_CORP cash") input("MEGA_CORP cash")
// We send it to another pubkey so that the transaction is not identical to the previous one // We send it to another pubkey so that the transaction is not identical to the previous one
output("MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE_PUBKEY)) output("MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE))
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() } command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }

View File

@ -2,13 +2,14 @@ package net.corda.contracts.asset
import net.corda.contracts.asset.Obligation.Lifecycle import net.corda.contracts.asset.Obligation.Lifecycle
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.NullPublicKey import net.corda.core.crypto.NULL_PARTY
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.testing.* import net.corda.testing.*
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.* import java.util.*
@ -33,18 +34,18 @@ class ObligationTests {
obligor = MEGA_CORP, obligor = MEGA_CORP,
template = megaCorpDollarSettlement, template = megaCorpDollarSettlement,
quantity = 1000.DOLLARS.quantity, quantity = 1000.DOLLARS.quantity,
beneficiary = DUMMY_PUBKEY_1 beneficiary = CHARLIE
) )
val outState = inState.copy(beneficiary = DUMMY_PUBKEY_2) val outState = inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2))
private fun cashObligationTestRoots( private fun cashObligationTestRoots(
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
) = group.apply { ) = group.apply {
unverifiedTransaction { unverifiedTransaction {
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)) output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY)) output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE))
output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB_PUBKEY)) output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB))
output("Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY) output("Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
} }
} }
@ -71,13 +72,13 @@ class ObligationTests {
tweak { tweak {
output { outState } output { outState }
output { outState `issued by` MINI_CORP } output { outState `issued by` MINI_CORP }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "at least one asset input" this `fails with` "at least one asset input"
} }
// Simple reallocation works. // Simple reallocation works.
tweak { tweak {
output { outState } output { outState }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this.verifies() this.verifies()
} }
} }
@ -98,7 +99,7 @@ class ObligationTests {
// institution is allowed to issue as much cash as they want. // institution is allowed to issue as much cash as they want.
transaction { transaction {
output { outState } output { outState }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Issue() } command(CHARLIE.owningKey) { Obligation.Commands.Issue() }
this `fails with` "output states are issued by a command signer" this `fails with` "output states are issued by a command signer"
} }
transaction { transaction {
@ -106,7 +107,7 @@ class ObligationTests {
Obligation.State( Obligation.State(
obligor = MINI_CORP, obligor = MINI_CORP,
quantity = 1000.DOLLARS.quantity, quantity = 1000.DOLLARS.quantity,
beneficiary = DUMMY_PUBKEY_1, beneficiary = CHARLIE,
template = megaCorpDollarSettlement template = megaCorpDollarSettlement
) )
} }
@ -121,14 +122,14 @@ class ObligationTests {
// Test generation works. // Test generation works.
val tx = TransactionType.General.Builder(notary = null).apply { val tx = TransactionType.General.Builder(notary = null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY) beneficiary = CHARLIE, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx }.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty()) assertTrue(tx.inputs.isEmpty())
val expected = Obligation.State( val expected = Obligation.State(
obligor = MINI_CORP, obligor = MINI_CORP,
quantity = 100.DOLLARS.quantity, quantity = 100.DOLLARS.quantity,
beneficiary = DUMMY_PUBKEY_1, beneficiary = CHARLIE,
template = megaCorpDollarSettlement template = megaCorpDollarSettlement
) )
assertEquals(tx.outputs[0].data, expected) assertEquals(tx.outputs[0].data, expected)
@ -142,7 +143,7 @@ class ObligationTests {
// Move fails: not allowed to summon money. // Move fails: not allowed to summon money.
tweak { tweak {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
@ -199,7 +200,7 @@ class ObligationTests {
// Issue some obligation // Issue some obligation
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) signWith(MINI_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -207,16 +208,16 @@ class ObligationTests {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY) val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
ptx.addInputState(tx.tx.outRef<Obligation.State<Currency>>(0)) ptx.addInputState(tx.tx.outRef<Obligation.State<Currency>>(0))
Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
} }
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */ /** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
@Test @Test
fun `generate close-out net transaction`() { fun `generate close-out net transaction`() {
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY) val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE_PUBKEY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) signWith(ALICE_KEY)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx }.toSignedTransaction().tx
@ -226,24 +227,24 @@ class ObligationTests {
/** Test generating a transaction to net two obligations of the different sizes, and confirm the balance is correct. */ /** Test generating a transaction to net two obligations of the different sizes, and confirm the balance is correct. */
@Test @Test
fun `generate close-out net transaction with remainder`() { fun `generate close-out net transaction with remainder`() {
val obligationAliceToBob = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB_PUBKEY) val obligationAliceToBob = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY) val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generateCloseOutNetting(this, ALICE_PUBKEY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) signWith(ALICE_KEY)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx }.toSignedTransaction().tx
assertEquals(1, tx.outputs.size) assertEquals(1, tx.outputs.size)
val actual = tx.outputs[0].data val actual = tx.outputs[0].data
assertEquals((1000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB_PUBKEY), actual) assertEquals((1000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB), actual)
} }
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */ /** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
@Test @Test
fun `generate payment net transaction`() { fun `generate payment net transaction`() {
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY) val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) signWith(ALICE_KEY)
@ -256,8 +257,8 @@ class ObligationTests {
/** Test generating a transaction to two obligations, where one is bigger than the other and therefore there is a remainder. */ /** Test generating a transaction to two obligations, where one is bigger than the other and therefore there is a remainder. */
@Test @Test
fun `generate payment net transaction with remainder`() { fun `generate payment net transaction with remainder`() {
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE_PUBKEY) val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(null).apply { val tx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice) Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY) signWith(ALICE_KEY)
@ -278,7 +279,7 @@ class ObligationTests {
// Generate a transaction issuing the obligation // Generate a transaction issuing the obligation
var tx = TransactionType.General.Builder(null).apply { var tx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement.copy(dueBefore = dueBefore), 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement.copy(dueBefore = dueBefore), 100.DOLLARS.quantity,
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) signWith(MINI_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
var stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0) var stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0)
@ -309,14 +310,14 @@ class ObligationTests {
@Test @Test
fun `generate settlement transaction`() { fun `generate settlement transaction`() {
val cashTx = TransactionType.General.Builder(null).apply { val cashTx = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP_PUBKEY, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction().tx }.toSignedTransaction().tx
// Generate a transaction issuing the obligation // Generate a transaction issuing the obligation
val obligationTx = TransactionType.General.Builder(null).apply { val obligationTx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity, Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY) beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
signWith(MINI_CORP_KEY) signWith(MINI_CORP_KEY)
}.toSignedTransaction().tx }.toSignedTransaction().tx
@ -354,7 +355,7 @@ class ObligationTests {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output("change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB_PUBKEY) } output("change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) }
command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies() this.verifies()
@ -368,7 +369,7 @@ class ObligationTests {
transaction("Issuance") { transaction("Issuance") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
output("change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB_PUBKEY) } output("change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) }
command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) } command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this `fails with` "amounts owed on input and output must match" this `fails with` "amounts owed on input and output must match"
@ -422,7 +423,7 @@ class ObligationTests {
transaction("Issuance") { transaction("Issuance") {
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE_PUBKEY) } output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) }
command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies() this.verifies()
@ -436,7 +437,7 @@ class ObligationTests {
transaction("Issuance") { transaction("Issuance") {
input("Bob's $1,000,000 obligation to Alice") input("Bob's $1,000,000 obligation to Alice")
input("MegaCorp's $1,000,000 obligation to Bob") input("MegaCorp's $1,000,000 obligation to Bob")
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE_PUBKEY) } output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) }
command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) } command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this `fails with` "all involved parties have signed" this `fails with` "all involved parties have signed"
@ -452,7 +453,7 @@ class ObligationTests {
transaction("Settlement") { transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Alice's $1,000,000") input("Alice's $1,000,000")
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY } output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this.verifies() this.verifies()
@ -463,10 +464,10 @@ class ObligationTests {
val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer
ledger { ledger {
transaction("Settlement") { transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)) input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY) input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) } output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) }
output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY } output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this.verifies() this.verifies()
@ -474,12 +475,12 @@ class ObligationTests {
} }
// Make sure we can't settle an obligation that's defaulted // Make sure we can't settle an obligation that's defaulted
val defaultedObligation: Obligation.State<Currency> = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)).copy(lifecycle = Lifecycle.DEFAULTED) val defaultedObligation: Obligation.State<Currency> = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED)
ledger { ledger {
transaction("Settlement") { transaction("Settlement") {
input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob
input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY) input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY } output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this `fails with` "all inputs are in the normal state" this `fails with` "all inputs are in the normal state"
@ -492,7 +493,7 @@ class ObligationTests {
transaction("Settlement") { transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
input("Alice's $1,000,000") input("Alice's $1,000,000")
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY } output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) } command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this `fails with` "amount in settle command" this `fails with` "amount in settle command"
@ -506,17 +507,17 @@ class ObligationTests {
val oneUnitFcoj = Amount(1, defaultFcoj) val oneUnitFcoj = Amount(1, defaultFcoj)
val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME) val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME)
val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE, val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE,
obligationDef, oneUnitFcoj.quantity, NullPublicKey) obligationDef, oneUnitFcoj.quantity, NULL_PARTY)
// Try settling a simple commodity obligation // Try settling a simple commodity obligation
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB_PUBKEY)) output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB))
output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE_PUBKEY)) output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE))
} }
transaction("Settlement") { transaction("Settlement") {
input("Alice's 1 FCOJ obligation to Bob") input("Alice's 1 FCOJ obligation to Bob")
input("Alice's 1 FCOJ") input("Alice's 1 FCOJ")
output("Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB_PUBKEY) } output("Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) } command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) }
command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation<Commodity>().legalContractReference) } command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation<Commodity>().legalContractReference) }
verifies() verifies()
@ -531,7 +532,7 @@ class ObligationTests {
cashObligationTestRoots(this) cashObligationTestRoots(this)
transaction("Settlement") { transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob") input("Alice's $1,000,000 obligation to Bob")
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)).copy(lifecycle = Lifecycle.DEFAULTED) } output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
this `fails with` "there is a timestamp from the authority" this `fails with` "there is a timestamp from the authority"
} }
@ -541,8 +542,8 @@ class ObligationTests {
val pastTestTime = TEST_TX_TIME - Duration.ofDays(7) val pastTestTime = TEST_TX_TIME - Duration.ofDays(7)
val futureTestTime = TEST_TX_TIME + Duration.ofDays(7) val futureTestTime = TEST_TX_TIME + Duration.ofDays(7)
transaction("Settlement") { transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` futureTestTime) input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime)
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this `fails with` "the due date has passed" this `fails with` "the due date has passed"
@ -551,8 +552,8 @@ class ObligationTests {
// Try defaulting an obligation that is now in the past // Try defaulting an obligation that is now in the past
ledger { ledger {
transaction("Settlement") { transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` pastTestTime) input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime)
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) } output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) } command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
this.verifies() this.verifies()
@ -565,7 +566,7 @@ class ObligationTests {
fun testMergeSplit() { fun testMergeSplit() {
// Splitting value works. // Splitting value works.
transaction { transaction {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
tweak { tweak {
input { inState } input { inState }
repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } } repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } }
@ -624,7 +625,7 @@ class ObligationTests {
inState.copy( inState.copy(
quantity = 15000, quantity = 15000,
template = megaCorpPoundSettlement, template = megaCorpPoundSettlement,
beneficiary = DUMMY_PUBKEY_2 beneficiary = AnonymousParty(DUMMY_PUBKEY_2)
) )
} }
output { outState.copy(quantity = 115000) } output { outState.copy(quantity = 115000) }
@ -635,7 +636,7 @@ class ObligationTests {
input { inState } input { inState }
input { inState `issued by` MINI_CORP } input { inState `issued by` MINI_CORP }
output { outState } output { outState }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
} }
@ -648,17 +649,17 @@ class ObligationTests {
output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) } output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
tweak { tweak {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) } command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
tweak { tweak {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token)) } command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token)) }
this `fails with` "required net.corda.core.contracts.FungibleAsset.Commands.Move command" this `fails with` "required net.corda.core.contracts.FungibleAsset.Commands.Move command"
tweak { tweak {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this.verifies() this.verifies()
} }
} }
@ -676,14 +677,14 @@ class ObligationTests {
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) } output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) }
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) } output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement))) } command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement))) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement))) } command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement))) }
this.verifies() this.verifies()
} }
} }
@ -697,20 +698,20 @@ class ObligationTests {
// Can't merge them together. // Can't merge them together.
tweak { tweak {
output { inState.copy(beneficiary = DUMMY_PUBKEY_2, quantity = 200000L) } output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2), quantity = 200000L) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// Missing MiniCorp deposit // Missing MiniCorp deposit
tweak { tweak {
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) } output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) } output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
this `fails with` "the amounts balance" this `fails with` "the amounts balance"
} }
// This works. // This works.
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) } output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) `issued by` MINI_CORP } output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) `issued by` MINI_CORP }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() } command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this.verifies() this.verifies()
} }
} }
@ -719,12 +720,12 @@ class ObligationTests {
fun multiCurrency() { fun multiCurrency() {
// Check we can do an atomic currency trade tx. // Check we can do an atomic currency trade tx.
transaction { transaction {
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, DUMMY_PUBKEY_2) val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(DUMMY_PUBKEY_2))
input { inState `owned by` DUMMY_PUBKEY_1 } input { inState `owned by` CHARLIE }
input { pounds } input { pounds }
output { inState `owned by` DUMMY_PUBKEY_2 } output { inState `owned by` AnonymousParty(DUMMY_PUBKEY_2) }
output { pounds `owned by` DUMMY_PUBKEY_1 } output { pounds `owned by` CHARLIE }
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Obligation.Commands.Move() } command(CHARLIE.owningKey, DUMMY_PUBKEY_2) { Obligation.Commands.Move() }
this.verifies() this.verifies()
} }
@ -733,11 +734,11 @@ class ObligationTests {
@Test @Test
fun `nettability of settlement contracts`() { fun `nettability of settlement contracts`() {
val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
5000.DOLLARS.quantity, MEGA_CORP_PUBKEY) 5000.DOLLARS.quantity, MEGA_CORP)
val twoKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, val twoKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
2000.DOLLARS.quantity, MINI_CORP_PUBKEY) 2000.DOLLARS.quantity, MINI_CORP)
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement, val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY) 1000.DOLLARS.quantity, MEGA_CORP)
// Obviously states must be nettable with themselves // Obviously states must be nettable with themselves
assertEquals(fiveKDollarsFromMegaToMega.bilateralNetState, fiveKDollarsFromMegaToMega.bilateralNetState) assertEquals(fiveKDollarsFromMegaToMega.bilateralNetState, fiveKDollarsFromMegaToMega.bilateralNetState)
@ -778,9 +779,9 @@ class ObligationTests {
@Test @Test
fun `extraction of issuance defintion`() { fun `extraction of issuance defintion`() {
val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
5000.DOLLARS.quantity, MEGA_CORP_PUBKEY) 5000.DOLLARS.quantity, MEGA_CORP)
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement, val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY) 1000.DOLLARS.quantity, MEGA_CORP)
// Issuance definitions must match the input // Issuance definitions must match the input
assertEquals(fiveKDollarsFromMegaToMega.template, megaCorpDollarSettlement) assertEquals(fiveKDollarsFromMegaToMega.template, megaCorpDollarSettlement)
@ -791,14 +792,14 @@ class ObligationTests {
fun `adding two settlement contracts nets them`() { fun `adding two settlement contracts nets them`() {
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm) val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
5000.DOLLARS.quantity, MINI_CORP_PUBKEY) 5000.DOLLARS.quantity, MINI_CORP)
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement, val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY) 1000.DOLLARS.quantity, MEGA_CORP)
var actual = fiveKDollarsFromMegaToMini.net(fiveKDollarsFromMegaToMini.copy(quantity = 2000.DOLLARS.quantity)) var actual = fiveKDollarsFromMegaToMini.net(fiveKDollarsFromMegaToMini.copy(quantity = 2000.DOLLARS.quantity))
// Both pay from mega to mini, so we add directly // Both pay from mega to mini, so we add directly
var expected = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, 7000.DOLLARS.quantity, var expected = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, 7000.DOLLARS.quantity,
MINI_CORP_PUBKEY) MINI_CORP)
assertEquals(expected, actual) assertEquals(expected, actual)
// Reversing the direction should mean adding the second state subtracts from the first // Reversing the direction should mean adding the second state subtracts from the first
@ -809,7 +810,7 @@ class ObligationTests {
// Trying to add an incompatible state must throw an error // Trying to add an incompatible state must throw an error
assertFailsWith(IllegalArgumentException::class) { assertFailsWith(IllegalArgumentException::class) {
fiveKDollarsFromMegaToMini.net(Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement, 1000.DOLLARS.quantity, fiveKDollarsFromMegaToMini.net(Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement, 1000.DOLLARS.quantity,
MINI_CORP_PUBKEY)) MINI_CORP))
} }
} }
@ -817,9 +818,9 @@ class ObligationTests {
fun `extracting amounts due between parties from a list of states`() { fun `extracting amounts due between parties from a list of states`() {
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm) val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
5000.DOLLARS.quantity, MINI_CORP_PUBKEY) 5000.DOLLARS.quantity, MINI_CORP)
val amount = fiveKDollarsFromMegaToMini.amount val amount = fiveKDollarsFromMegaToMini.amount
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<Currency>>> = mapOf(Pair(Pair(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Amount(amount.quantity, amount.token.product))) val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Obligation.Terms<Currency>>> = mapOf(Pair(Pair(MEGA_CORP, MINI_CORP), Amount(amount.quantity, amount.token.product)))
val actual = extractAmountsDue(megaCorpDollarSettlement, listOf(fiveKDollarsFromMegaToMini)) val actual = extractAmountsDue(megaCorpDollarSettlement, listOf(fiveKDollarsFromMegaToMini))
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -827,24 +828,24 @@ class ObligationTests {
@Test @Test
fun `netting equal balances due between parties`() { fun `netting equal balances due between parties`() {
// Now try it with two balances, which cancel each other out // Now try it with two balances, which cancel each other out
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf( val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(ALICE, BOB), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) Pair(Pair(BOB, ALICE), Amount(100000000, GBP))
) )
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
val actual: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = netAmountsDue(balanced) val actual: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = netAmountsDue(balanced)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun `netting difference balances due between parties`() { fun `netting difference balances due between parties`() {
// Now try it with two balances, which cancel each other out // Now try it with two balances, which cancel each other out
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf( val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(ALICE, BOB), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(200000000, GBP)) Pair(Pair(BOB, ALICE), Amount(200000000, GBP))
) )
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf( val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) Pair(Pair(BOB, ALICE), Amount(100000000, GBP))
) )
val actual = netAmountsDue(balanced) val actual = netAmountsDue(balanced)
assertEquals(expected, actual) assertEquals(expected, actual)
@ -852,16 +853,16 @@ class ObligationTests {
@Test @Test
fun `summing empty balances due between parties`() { fun `summing empty balances due between parties`() {
val empty = emptyMap<Pair<PublicKey, PublicKey>, Amount<Currency>>() val empty = emptyMap<Pair<AbstractParty, AbstractParty>, Amount<Currency>>()
val expected = emptyMap<PublicKey, Long>() val expected = emptyMap<AbstractParty, Long>()
val actual = sumAmountsDue(empty) val actual = sumAmountsDue(empty)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun `summing balances due between parties`() { fun `summing balances due between parties`() {
val simple: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP))) val simple: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(Pair(Pair(ALICE, BOB), Amount(100000000, GBP)))
val expected: Map<PublicKey, Long> = mapOf(Pair(ALICE_PUBKEY, -100000000L), Pair(BOB_PUBKEY, 100000000L)) val expected: Map<AbstractParty, Long> = mapOf(Pair(ALICE, -100000000L), Pair(BOB, 100000000L))
val actual = sumAmountsDue(simple) val actual = sumAmountsDue(simple)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -869,11 +870,11 @@ class ObligationTests {
@Test @Test
fun `summing balances due between parties which net to zero`() { fun `summing balances due between parties which net to zero`() {
// Now try it with two balances, which cancel each other out // Now try it with two balances, which cancel each other out
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf( val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(ALICE, BOB), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) Pair(Pair(BOB, ALICE), Amount(100000000, GBP))
) )
val expected: Map<PublicKey, Long> = emptyMap() // Zero balances are stripped before returning val expected: Map<AbstractParty, Long> = emptyMap() // Zero balances are stripped before returning
val actual = sumAmountsDue(balanced) val actual = sumAmountsDue(balanced)
assertEquals(expected, actual) assertEquals(expected, actual)
} }

View File

@ -10,6 +10,7 @@ import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.NullSignature import net.corda.core.crypto.NullSignature
import net.corda.core.identity.AnonymousParty
import net.corda.core.testing.* import net.corda.core.testing.*
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
@ -25,7 +26,7 @@ class ContractStateGenerator : Generator<ContractState>(ContractState::class.jav
override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState { override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState {
return Cash.State( return Cash.State(
amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status), amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status),
owner = PublicKeyGenerator().generate(random, status) owner = AnonymousParty(PublicKeyGenerator().generate(random, status))
) )
} }
} }

View File

@ -54,8 +54,8 @@ class CashPaymentFlowTests {
net.runNetwork() net.runNetwork()
val paymentTx = future.getOrThrow() val paymentTx = future.getOrThrow()
val states = paymentTx.tx.outputs.map { it.data }.filterIsInstance<Cash.State>() val states = paymentTx.tx.outputs.map { it.data }.filterIsInstance<Cash.State>()
val ourState = states.single { it.owner != payTo.owningKey } val ourState = states.single { it.owner.owningKey != payTo.owningKey }
val paymentState = states.single { it.owner == payTo.owningKey } val paymentState = states.single { it.owner.owningKey == payTo.owningKey }
assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), paymentState.amount) assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), paymentState.amount)
} }

View File

@ -11,7 +11,11 @@ import io.requery.rx.KotlinRxEntityStore
import io.requery.sql.* import io.requery.sql.*
import io.requery.sql.platform.Generic import io.requery.sql.platform.Generic
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.schemas.requery.converters.InstantConverter import net.corda.core.schemas.requery.converters.InstantConverter
@ -19,17 +23,16 @@ import net.corda.core.schemas.requery.converters.VaultStateStatusConverter
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.BOB
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.core.utilities.DUMMY_PUBKEY_2
import org.h2.jdbcx.JdbcDataSource import org.h2.jdbcx.JdbcDataSource
import org.junit.After import org.junit.After
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import rx.Observable import rx.Observable
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@ -81,12 +84,12 @@ class VaultSchemaTest {
private class VaultNoopContract : Contract { private class VaultNoopContract : Contract {
override val legalContractReference = SecureHash.sha256("") override val legalContractReference = SecureHash.sha256("")
data class VaultNoopState(override val owner: PublicKey) : OwnableState { data class VaultNoopState(override val owner: AbstractParty) : OwnableState {
override val contract = VaultNoopContract() override val contract = VaultNoopContract()
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Create(), copy(owner = newOwner)) override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Create(), copy(owner = newOwner))
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -101,10 +104,10 @@ class VaultSchemaTest {
private fun setupDummyData() { private fun setupDummyData() {
// dummy Transaction // dummy Transaction
val notary: Party = DUMMY_NOTARY val notary: Party = DUMMY_NOTARY
val inState1 = TransactionState(DummyContract.SingleOwnerState(0, DUMMY_PUBKEY_1), notary) val inState1 = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary)
val inState2 = TransactionState(DummyContract.MultiOwnerState(0, val inState2 = TransactionState(DummyContract.MultiOwnerState(0,
listOf(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2)), notary) listOf(ALICE, BOB)), notary)
val inState3 = TransactionState(VaultNoopContract.VaultNoopState(DUMMY_PUBKEY_1), notary) val inState3 = TransactionState(VaultNoopContract.VaultNoopState(ALICE), notary)
val outState1 = inState1.copy() val outState1 = inState1.copy()
val outState2 = inState2.copy() val outState2 = inState2.copy()
val outState3 = inState3.copy() val outState3 = inState3.copy()
@ -132,9 +135,9 @@ class VaultSchemaTest {
private fun createTxnWithTwoStateTypes(): LedgerTransaction { private fun createTxnWithTwoStateTypes(): LedgerTransaction {
val notary: Party = DUMMY_NOTARY val notary: Party = DUMMY_NOTARY
val inState1 = TransactionState(DummyContract.SingleOwnerState(0, DUMMY_PUBKEY_1), notary) val inState1 = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary)
val inState2 = TransactionState(DummyContract.MultiOwnerState(0, val inState2 = TransactionState(DummyContract.MultiOwnerState(0,
listOf(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2)), notary) listOf(ALICE, BOB)), notary)
val outState1 = inState1.copy() val outState1 = inState1.copy()
val outState2 = inState2.copy() val outState2 = inState2.copy()
val state1TxHash = SecureHash.randomSHA256() val state1TxHash = SecureHash.randomSHA256()

View File

@ -57,7 +57,7 @@ class BFTNotaryServiceTests : NodeBasedTest() {
firstSpend.resultFuture.getOrThrow() firstSpend.resultFuture.getOrThrow()
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run { val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity.owningKey) val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity)
addOutputState(dummyState) addOutputState(dummyState)
signWith(aliceKey) signWith(aliceKey)
toSignedTransaction(false) toSignedTransaction(false)

View File

@ -46,7 +46,7 @@ class RaftNotaryServiceTests : NodeBasedTest() {
firstSpend.resultFuture.getOrThrow() firstSpend.resultFuture.getOrThrow()
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run { val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity.owningKey) val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity)
addOutputState(dummyState) addOutputState(dummyState)
signWith(aliceKey) signWith(aliceKey)
toSignedTransaction(false) toSignedTransaction(false)

View File

@ -115,7 +115,7 @@ class ContractUpgradeHandler(otherSide: Party) : AbstractStateReplacementFlow.Ac
val proposedTx = proposal.stx.tx val proposedTx = proposal.stx.tx
val expectedTx = ContractUpgradeFlow.assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction() val expectedTx = ContractUpgradeFlow.assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction()
requireThat { requireThat {
"The instigator is one of the participants" using (otherSide.owningKey in oldStateAndRef.state.data.participants) "The instigator is one of the participants" using (otherSide in oldStateAndRef.state.data.participants)
"The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification == authorisedUpgrade) "The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification == authorisedUpgrade)
"The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx) "The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx)
} }

View File

@ -3,6 +3,7 @@ package net.corda.node.services.identity
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.requireThat import net.corda.core.contracts.requireThat
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
@ -16,7 +17,6 @@ import java.security.cert.*
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import javax.security.auth.x500.X500Principal
/** /**
* Simple identity service which caches parties and provides functionality for efficient lookup. * Simple identity service which caches parties and provides functionality for efficient lookup.
@ -44,7 +44,7 @@ class InMemoryIdentityService : SingletonSerializeAsToken(), IdentityService {
@Deprecated("Use partyFromX500Name") @Deprecated("Use partyFromX500Name")
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)] override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal] override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]
override fun partyFromAnonymous(party: AnonymousParty): Party? = partyFromKey(party.owningKey) override fun partyFromAnonymous(party: AbstractParty): Party? = partyFromKey(party.owningKey)
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
@Throws(IdentityService.UnknownAnonymousPartyException::class) @Throws(IdentityService.UnknownAnonymousPartyException::class)

View File

@ -11,11 +11,12 @@ import io.requery.kotlin.notNull
import io.requery.query.RowExpression import io.requery.query.RowExpression
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.OnLedgerAsset import net.corda.contracts.asset.OnLedgerAsset
import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.bufferUntilSubscribed import net.corda.core.bufferUntilSubscribed
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
@ -468,7 +469,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
@Suspendable @Suspendable
override fun generateSpend(tx: TransactionBuilder, override fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: PublicKey, to: AbstractParty,
onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<PublicKey>> { onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<PublicKey>> {
// Retrieve unspent and unlocked cash states that meet our spending criteria. // Retrieve unspent and unlocked cash states that meet our spending criteria.
val acceptableCoins = unconsumedStatesForSpending<Cash.State>(amount, onlyFromParties, tx.notary, tx.lockId) val acceptableCoins = unconsumedStatesForSpending<Cash.State>(amount, onlyFromParties, tx.notary, tx.lockId)
@ -477,7 +478,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
{ Cash().generateMoveCommand() }) { Cash().generateMoveCommand() })
} }
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKey) private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: AbstractParty)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))
private fun makeUpdate(tx: WireTransaction, ourKeys: Set<PublicKey>): Vault.Update { private fun makeUpdate(tx: WireTransaction, ourKeys: Set<PublicKey>): Vault.Update {
@ -528,7 +529,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
} }
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>) = when (state) { private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>) = when (state) {
is OwnableState -> state.owner.containsAny(ourKeys) is OwnableState -> state.owner.owningKey.containsAny(ourKeys)
// It's potentially of interest to the vault // It's potentially of interest to the vault
is LinearState -> state.isRelevant(ourKeys) is LinearState -> state.isRelevant(ourKeys)
else -> false else -> false

View File

@ -91,7 +91,7 @@ class CordaRPCOpsImplTest {
val expectedState = Cash.State(Amount(quantity, val expectedState = Cash.State(Amount(quantity,
Issued(aliceNode.info.legalIdentity.ref(ref), GBP)), Issued(aliceNode.info.legalIdentity.ref(ref), GBP)),
recipient.owningKey) recipient)
var issueSmId: StateMachineRunId? = null var issueSmId: StateMachineRunId? = null
stateMachineUpdates.expectEvents { stateMachineUpdates.expectEvents {

View File

@ -14,6 +14,7 @@ import net.corda.core.flows.FlowStateMachine
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.map import net.corda.core.map
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
@ -97,7 +98,7 @@ class TwoPartyTradeFlowTests {
} }
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, fillUpForSeller(false, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
} }
@ -144,7 +145,7 @@ class TwoPartyTradeFlowTests {
} }
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, fillUpForSeller(false, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
} }
@ -195,7 +196,7 @@ class TwoPartyTradeFlowTests {
bobNode.services.fillWithSomeTestCash(2000.DOLLARS, outputNotary = notaryNode.info.notaryIdentity) bobNode.services.fillWithSomeTestCash(2000.DOLLARS, outputNotary = notaryNode.info.notaryIdentity)
} }
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, fillUpForSeller(false, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
} }
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey) insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey)
@ -301,6 +302,7 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name) val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name) val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name) val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name)
val alice = aliceNode.info.legalIdentity
val aliceKey = aliceNode.services.legalIdentityKey val aliceKey = aliceNode.services.legalIdentityKey
ledger(aliceNode.services) { ledger(aliceNode.services) {
@ -317,12 +319,13 @@ class TwoPartyTradeFlowTests {
} }
val extraKey = bobNode.keyManagement.freshKey() val extraKey = bobNode.keyManagement.freshKey()
val bobsFakeCash = fillUpForBuyer(false, extraKey.public, val extraPublicKey = extraKey.public
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(extraPublicKey),
DUMMY_CASH_ISSUER.party, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey) val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey)
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, fillUpForSeller(false, alice,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
} }
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey) val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey)
@ -416,13 +419,14 @@ class TwoPartyTradeFlowTests {
attachment(ByteArrayInputStream(stream.toByteArray())) attachment(ByteArrayInputStream(stream.toByteArray()))
} }
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public, val bobsKey = bobNode.keyManagement.freshKey().public
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(bobsKey),
DUMMY_CASH_ISSUER.party, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode) insertFakeTransactions(bobsFakeCash, bobNode, notaryNode)
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, fillUpForSeller(false, aliceNode.info.legalIdentity,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
} }
@ -520,14 +524,16 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name) val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name) val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name) val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
val alice = aliceNode.info.legalIdentity
val aliceKey = aliceNode.services.legalIdentityKey val aliceKey = aliceNode.services.legalIdentityKey
val bob = bobNode.info.legalIdentity
val bobKey = bobNode.services.legalIdentityKey val bobKey = bobNode.services.legalIdentityKey
val issuer = MEGA_CORP.ref(1, 2, 3) val issuer = MEGA_CORP.ref(1, 2, 3)
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public, DUMMY_CASH_ISSUER.party, val bobsBadCash = fillUpForBuyer(bobError, bob, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
val alicesFakePaper = aliceNode.database.transaction { val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey, fillUpForSeller(aliceError, alice,
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
} }
@ -571,16 +577,16 @@ class TwoPartyTradeFlowTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer( private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
withError: Boolean, withError: Boolean,
owner: PublicKey, owner: AbstractParty,
issuer: AnonymousParty, issuer: AbstractParty,
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> { notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
val interimOwnerKey = MEGA_CORP_PUBKEY val interimOwner = MEGA_CORP
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she // Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
// wants to sell to Bob. // wants to sell to Bob.
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
// Issued money to itself. // Issued money to itself.
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwnerKey } output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` interimOwnerKey } output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
if (!withError) { if (!withError) {
command(issuer.owningKey) { Cash.Commands.Issue() } command(issuer.owningKey) { Cash.Commands.Issue() }
} else { } else {
@ -599,15 +605,15 @@ class TwoPartyTradeFlowTests {
val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 1") input("elbonian money 1")
output("bob cash 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` owner } output("bob cash 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` owner }
command(interimOwnerKey) { Cash.Commands.Move() } command(interimOwner.owningKey) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) { val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 2") input("elbonian money 2")
output("bob cash 2", notary = notary) { 300.DOLLARS.CASH `issued by` issuer `owned by` owner } output("bob cash 2", notary = notary) { 300.DOLLARS.CASH `issued by` issuer `owned by` owner }
output(notary = notary) { 700.DOLLARS.CASH `issued by` issuer `owned by` interimOwnerKey } // Change output. output(notary = notary) { 700.DOLLARS.CASH `issued by` issuer `owned by` interimOwner } // Change output.
command(interimOwnerKey) { Cash.Commands.Move() } command(interimOwner.owningKey) { Cash.Commands.Move() }
this.verifies() this.verifies()
} }
@ -617,7 +623,7 @@ class TwoPartyTradeFlowTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller( private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
withError: Boolean, withError: Boolean,
owner: PublicKey, owner: AbstractParty,
amount: Amount<Issued<Currency>>, amount: Amount<Issued<Currency>>,
attachmentID: SecureHash?, attachmentID: SecureHash?,
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> { notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {

View File

@ -123,9 +123,9 @@ class NotaryChangeTests {
val owner = node.info.legalIdentity.ref(0) val owner = node.info.legalIdentity.ref(0)
val notary = notaryNode.info.notaryIdentity val notary = notaryNode.info.notaryIdentity
val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey) val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
val stateB = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey) val stateB = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
val stateC = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey) val stateC = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
val tx = TransactionType.General.Builder(null).apply { val tx = TransactionType.General.Builder(null).apply {
addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey)) addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey))
@ -162,7 +162,7 @@ fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> {
fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> { fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> {
val state = TransactionState(DummyContract.MultiOwnerState(0, val state = TransactionState(DummyContract.MultiOwnerState(0,
listOf(nodeA.info.legalIdentity.owningKey, nodeB.info.legalIdentity.owningKey)), notaryNode.info.notaryIdentity) listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), notaryNode.info.notaryIdentity)
val tx = TransactionType.NotaryChange.Builder(notaryNode.info.notaryIdentity).withItems(state) val tx = TransactionType.NotaryChange.Builder(notaryNode.info.notaryIdentity).withItems(state)
val nodeAKey = nodeA.services.legalIdentityKey val nodeAKey = nodeA.services.legalIdentityKey
val nodeBKey = nodeB.services.legalIdentityKey val nodeBKey = nodeB.services.legalIdentityKey

View File

@ -10,6 +10,7 @@ import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.NullPublicKey import net.corda.core.crypto.NullPublicKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo import net.corda.core.serialization.storageKryo
@ -129,7 +130,7 @@ class RequeryConfigurationTest {
index = txnState.index index = txnState.index
stateStatus = Vault.StateStatus.UNCONSUMED stateStatus = Vault.StateStatus.UNCONSUMED
contractStateClassName = DummyContract.SingleOwnerState::class.java.name contractStateClassName = DummyContract.SingleOwnerState::class.java.name
contractState = DummyContract.SingleOwnerState(owner = DUMMY_PUBKEY_1).serialize(storageKryo()).bytes contractState = DummyContract.SingleOwnerState(owner = AnonymousParty(DUMMY_PUBKEY_1)).serialize(storageKryo()).bytes
notaryName = txn.tx.notary!!.name.toString() notaryName = txn.tx.notary!!.name.toString()
notaryKey = txn.tx.notary!!.owningKey.toBase58String() notaryKey = txn.tx.notary!!.owningKey.toBase58String()
recordedTime = Instant.now() recordedTime = Instant.now()

View File

@ -5,6 +5,7 @@ import net.corda.core.days
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRef import net.corda.core.flows.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
@ -113,7 +114,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
} }
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState { class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState {
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
override val linearId = UniqueIdentifier() override val linearId = UniqueIdentifier()

View File

@ -2,12 +2,13 @@ package net.corda.node.services.events
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.Party
import net.corda.core.crypto.containsAny import net.corda.core.crypto.containsAny
import net.corda.core.flows.FlowInitiator import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.flows.SchedulableFlow import net.corda.core.flows.SchedulableFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.linearHeadsOfType import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
@ -46,10 +47,10 @@ class ScheduledFlowTests {
} }
} }
override val participants: List<PublicKey> = listOf(source.owningKey, destination.owningKey) override val participants: List<AbstractParty> = listOf(source, destination)
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { it.containsAny(ourKeys) } return participants.any { it.owningKey.containsAny(ourKeys) }
} }
} }

View File

@ -37,7 +37,7 @@ class DataVendingServiceTests {
@Test @Test
fun `notify of transaction`() { fun `notify of transaction`() {
val (vaultServiceNode, registerNode) = network.createTwoNodes() val (vaultServiceNode, registerNode) = network.createTwoNodes()
val beneficiary = vaultServiceNode.info.legalIdentity.owningKey val beneficiary = vaultServiceNode.info.legalIdentity
val deposit = registerNode.info.legalIdentity.ref(1) val deposit = registerNode.info.legalIdentity.ref(1)
network.runNetwork() network.runNetwork()
@ -67,7 +67,7 @@ class DataVendingServiceTests {
@Test @Test
fun `notify failure`() { fun `notify failure`() {
val (vaultServiceNode, registerNode) = network.createTwoNodes() val (vaultServiceNode, registerNode) = network.createTwoNodes()
val beneficiary = vaultServiceNode.info.legalIdentity.owningKey val beneficiary = vaultServiceNode.info.legalIdentity
val deposit = MEGA_CORP.ref(1) val deposit = MEGA_CORP.ref(1)
network.runNetwork() network.runNetwork()

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
@ -86,7 +87,7 @@ class HibernateObserverTests {
override val contract: Contract override val contract: Contract
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
override val participants: List<CompositeKey> override val participants: List<AbstractParty>
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
} }

View File

@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.DUMMY_CASH_ISSUER import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.StatesNotAvailableException import net.corda.core.node.services.StatesNotAvailableException
import net.corda.core.node.services.TxWritableStorageService import net.corda.core.node.services.TxWritableStorageService
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
@ -401,11 +402,11 @@ class NodeVaultServiceTest {
fun addNoteToTransaction() { fun addNoteToTransaction() {
database.transaction { database.transaction {
val freshKey = services.legalIdentityKey val freshKey = services.legalIdentityKey.public
// Issue a txn to Send us some Money // Issue a txn to Send us some Money
val usefulTX = TransactionType.General.Builder(null).apply { val usefulTX = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -418,7 +419,7 @@ class NodeVaultServiceTest {
// Issue more Money (GBP) // Issue more Money (GBP)
val anotherTX = TransactionType.General.Builder(null).apply { val anotherTX = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY) Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()

View File

@ -206,9 +206,9 @@ class VaultQueryTests {
fun `unconsumed states by participants`() { fun `unconsumed states by participants`() {
database.transaction { database.transaction {
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"), participants = listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY)) services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"), participants = listOf(MEGA_CORP, MINI_CORP))
services.fillWithSomeTestDeals(listOf("456"), 3, participants = listOf(MEGA_CORP_PUBKEY, BIG_CORP_PUBKEY)) services.fillWithSomeTestDeals(listOf("456"), 3, participants = listOf(MEGA_CORP, BIG_CORP))
services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP_PUBKEY, MINI_CORP_PUBKEY)) services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP, MINI_CORP))
// DOCSTART VaultQueryExample5 // DOCSTART VaultQueryExample5
val criteria = VaultQueryCriteria(participantIdentities = listOf(MEGA_CORP.name, MINI_CORP.name)) val criteria = VaultQueryCriteria(participantIdentities = listOf(MEGA_CORP.name, MINI_CORP.name))

View File

@ -4,17 +4,17 @@ import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.DUMMY_CASH_ISSUER import net.corda.contracts.asset.DUMMY_CASH_ISSUER
import net.corda.contracts.testing.* import net.corda.contracts.testing.*
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.node.services.consumedStates import net.corda.core.node.services.consumedStates
import net.corda.core.node.services.unconsumedStates import net.corda.core.node.services.unconsumedStates
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.BOB_KEY import net.corda.core.utilities.BOB
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.utilities.LogHelper import net.corda.core.utilities.LogHelper
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction import net.corda.node.utilities.transaction
import net.corda.testing.BOB_PUBKEY
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
@ -79,7 +79,7 @@ class VaultWithCashTest {
val state = w[0].state.data val state = w[0].state.data
assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount) assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount)
assertEquals(services.key.public, state.owner) assertEquals(services.key.public, state.owner.owningKey)
assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[2].state.data).amount) assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[2].state.data).amount)
assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[1].state.data).amount) assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[1].state.data).amount)
@ -91,8 +91,9 @@ class VaultWithCashTest {
database.transaction { database.transaction {
// A tx that sends us money. // A tx that sends us money.
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val usefulTX = TransactionType.General.Builder(null).apply { val usefulTX = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshPublicKey), DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -101,7 +102,7 @@ class VaultWithCashTest {
// A tx that spends our money. // A tx that spends our money.
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply { val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY) vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -110,7 +111,7 @@ class VaultWithCashTest {
// A tx that doesn't send us anything. // A tx that doesn't send us anything.
val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply { val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_KEY.public, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -128,13 +129,14 @@ class VaultWithCashTest {
@Test @Test
fun `issue and attempt double spend`() { fun `issue and attempt double spend`() {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
database.transaction { database.transaction {
// A tx that sends us money. // A tx that sends us money.
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L), services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L),
issuedBy = MEGA_CORP.ref(1), issuedBy = MEGA_CORP.ref(1),
issuerKey = MEGA_CORP_KEY, issuerKey = MEGA_CORP_KEY,
ownedBy = freshKey.public) ownedBy = AnonymousParty(freshPublicKey))
println("Cash balance: ${vault.cashBalances[USD]}") println("Cash balance: ${vault.cashBalances[USD]}")
assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10) assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10)
@ -149,7 +151,7 @@ class VaultWithCashTest {
try { try {
val txn1 = val txn1 =
TransactionType.General.Builder(DUMMY_NOTARY).apply { TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 60.DOLLARS, BOB_PUBKEY) vault.generateSpend(this, 60.DOLLARS, BOB)
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -181,7 +183,7 @@ class VaultWithCashTest {
try { try {
val txn2 = val txn2 =
TransactionType.General.Builder(DUMMY_NOTARY).apply { TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY) vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -219,12 +221,14 @@ class VaultWithCashTest {
fun `branching LinearStates fails to verify`() { fun `branching LinearStates fails to verify`() {
database.transaction { database.transaction {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val freshIdentity = AnonymousParty(freshPublicKey)
val linearId = UniqueIdentifier() val linearId = UniqueIdentifier()
// Issue a linear state // Issue a linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -239,12 +243,14 @@ class VaultWithCashTest {
fun `sequencing LinearStates works`() { fun `sequencing LinearStates works`() {
database.transaction { database.transaction {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val freshIdentity = AnonymousParty(freshPublicKey)
val linearId = UniqueIdentifier() val linearId = UniqueIdentifier()
// Issue a linear state // Issue a linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -256,7 +262,7 @@ class VaultWithCashTest {
// Move the same state // Move the same state
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
addInputState(dummyIssue.tx.outRef<LinearState>(0)) addInputState(dummyIssue.tx.outRef<LinearState>(0))
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -272,8 +278,9 @@ class VaultWithCashTest {
fun `spending cash in vault of mixed state types works`() { fun `spending cash in vault of mixed state types works`() {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
database.transaction { database.transaction {
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = freshKey.public) services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = AnonymousParty(freshPublicKey))
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L)) services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L))
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L)) services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
val cash = vault.unconsumedStates<Cash.State>() val cash = vault.unconsumedStates<Cash.State>()
@ -287,7 +294,7 @@ class VaultWithCashTest {
database.transaction { database.transaction {
// A tx that spends our money. // A tx that spends our money.
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply { val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY) vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -305,6 +312,8 @@ class VaultWithCashTest {
fun `consuming multiple contract state types in same transaction`() { fun `consuming multiple contract state types in same transaction`() {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val freshIdentity = AnonymousParty(freshPublicKey)
database.transaction { database.transaction {
services.fillWithSomeTestDeals(listOf("123", "456", "789")) services.fillWithSomeTestDeals(listOf("123", "456", "789"))
@ -317,8 +326,8 @@ class VaultWithCashTest {
// Create a txn consuming different contract types // Create a txn consuming different contract types
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity)))
addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshKey.public))) addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)))
addInputState(linearStates.first()) addInputState(linearStates.first())
addInputState(deals.first()) addInputState(deals.first())
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)

View File

@ -13,6 +13,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startTrackedFlow import net.corda.core.messaging.startTrackedFlow
import net.corda.core.sizedInputStreamAndHash import net.corda.core.sizedInputStreamAndHash
@ -179,6 +180,6 @@ class AttachmentContract : Contract {
data class State(val hash: SecureHash.SHA256) : ContractState { data class State(val hash: SecureHash.SHA256) : ContractState {
override val contract: Contract = AttachmentContract() override val contract: Contract = AttachmentContract()
override val participants: List<PublicKey> = emptyList() override val participants: List<AbstractParty> = emptyList()
} }
} }

View File

@ -70,7 +70,7 @@ class IRSDemoTest : IntegrationTestCategory {
val vaultUpdates = proxy.vaultAndUpdates().second val vaultUpdates = proxy.vaultAndUpdates().second
val fixingDates = vaultUpdates.map { update -> val fixingDates = vaultUpdates.map { update ->
val irsStates = update.produced.map { it.state.data }.filterIsInstance<InterestRateSwap.State<*>>() val irsStates = update.produced.map { it.state.data }.filterIsInstance<InterestRateSwap.State>()
irsStates.mapNotNull { it.calculation.nextFixingDate() }.max() irsStates.mapNotNull { it.calculation.nextFixingDate() }.max()
}.cache().toBlocking() }.cache().toBlocking()

View File

@ -40,22 +40,22 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
private val logger = loggerFor<InterestRateSwapAPI>() private val logger = loggerFor<InterestRateSwapAPI>()
private fun generateDealLink(deal: InterestRateSwap.State<*>) = "/api/irs/deals/" + deal.common.tradeID private fun generateDealLink(deal: InterestRateSwap.State) = "/api/irs/deals/" + deal.common.tradeID
private fun getDealByRef(ref: String): InterestRateSwap.State<*>? { private fun getDealByRef(ref: String): InterestRateSwap.State? {
val (vault, vaultUpdates) = rpc.vaultAndUpdates() val (vault, vaultUpdates) = rpc.vaultAndUpdates()
vaultUpdates.notUsed() vaultUpdates.notUsed()
val states = vault.filterStatesOfType<InterestRateSwap.State<*>>().filter { it.state.data.ref == ref } val states = vault.filterStatesOfType<InterestRateSwap.State>().filter { it.state.data.ref == ref }
return if (states.isEmpty()) null else { return if (states.isEmpty()) null else {
val deals = states.map { it.state.data } val deals = states.map { it.state.data }
return if (deals.isEmpty()) null else deals[0] return if (deals.isEmpty()) null else deals[0]
} }
} }
private fun getAllDeals(): Array<InterestRateSwap.State<*>> { private fun getAllDeals(): Array<InterestRateSwap.State> {
val (vault, vaultUpdates) = rpc.vaultAndUpdates() val (vault, vaultUpdates) = rpc.vaultAndUpdates()
vaultUpdates.notUsed() vaultUpdates.notUsed()
val states = vault.filterStatesOfType<InterestRateSwap.State<*>>() val states = vault.filterStatesOfType<InterestRateSwap.State>()
val swaps = states.map { it.state.data }.toTypedArray() val swaps = states.map { it.state.data }.toTypedArray()
return swaps return swaps
} }
@ -63,14 +63,14 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
@GET @GET
@Path("deals") @Path("deals")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
fun fetchDeals(): Array<InterestRateSwap.State<*>> = getAllDeals() fun fetchDeals(): Array<InterestRateSwap.State> = getAllDeals()
@POST @POST
@Path("deals") @Path("deals")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
fun storeDeal(newDeal: InterestRateSwap.State<Party>): Response { fun storeDeal(newDeal: InterestRateSwap.State): Response {
return try { return try {
rpc.startFlow(AutoOfferFlow::Requester, newDeal.toAnonymous()).returnValue.getOrThrow() rpc.startFlow(AutoOfferFlow::Requester, newDeal).returnValue.getOrThrow()
Response.created(URI.create(generateDealLink(newDeal))).build() Response.created(URI.create(generateDealLink(newDeal))).build()
} catch (ex: Throwable) { } catch (ex: Throwable) {
logger.info("Exception when creating deal: $ex") logger.info("Exception when creating deal: $ex")

View File

@ -2,7 +2,8 @@ package net.corda.irs.contract
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.* import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.* import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
@ -307,8 +308,8 @@ class InterestRateSwap : Contract {
} }
@CordaSerializable @CordaSerializable
open class FixedLeg<P : AbstractParty>( open class FixedLeg(
var fixedRatePayer: P, var fixedRatePayer: AbstractParty,
notional: Amount<Currency>, notional: Amount<Currency>,
paymentFrequency: Frequency, paymentFrequency: Frequency,
effectiveDate: LocalDate, effectiveDate: LocalDate,
@ -335,7 +336,7 @@ class InterestRateSwap : Contract {
if (other?.javaClass != javaClass) return false if (other?.javaClass != javaClass) return false
if (!super.equals(other)) return false if (!super.equals(other)) return false
other as FixedLeg<*> other as FixedLeg
if (fixedRatePayer != other.fixedRatePayer) return false if (fixedRatePayer != other.fixedRatePayer) return false
if (fixedRate != other.fixedRate) return false if (fixedRate != other.fixedRate) return false
@ -347,7 +348,7 @@ class InterestRateSwap : Contract {
override fun hashCode() = super.hashCode() + 31 * Objects.hash(fixedRatePayer, fixedRate, rollConvention) override fun hashCode() = super.hashCode() + 31 * Objects.hash(fixedRatePayer, fixedRate, rollConvention)
// Can't autogenerate as not a data class :-( // Can't autogenerate as not a data class :-(
fun copy(fixedRatePayer: P = this.fixedRatePayer, fun copy(fixedRatePayer: AbstractParty = this.fixedRatePayer,
notional: Amount<Currency> = this.notional, notional: Amount<Currency> = this.notional,
paymentFrequency: Frequency = this.paymentFrequency, paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate, effectiveDate: LocalDate = this.effectiveDate,
@ -365,17 +366,11 @@ class InterestRateSwap : Contract {
fixedRatePayer, notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, fixedRatePayer, notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate,
terminationDateAdjustment, dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, terminationDateAdjustment, dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay,
paymentCalendar, interestPeriodAdjustment, fixedRate, rollConvention) paymentCalendar, interestPeriodAdjustment, fixedRate, rollConvention)
fun toAnonymous(): FixedLeg<AnonymousParty> {
return FixedLeg(fixedRatePayer.toAnonymous(), notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, terminationDateAdjustment,
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment,
fixedRate, rollConvention)
}
} }
@CordaSerializable @CordaSerializable
open class FloatingLeg<P : AbstractParty>( open class FloatingLeg(
var floatingRatePayer: P, var floatingRatePayer: AbstractParty,
notional: Amount<Currency>, notional: Amount<Currency>,
paymentFrequency: Frequency, paymentFrequency: Frequency,
effectiveDate: LocalDate, effectiveDate: LocalDate,
@ -411,7 +406,7 @@ class InterestRateSwap : Contract {
if (other?.javaClass != javaClass) return false if (other?.javaClass != javaClass) return false
if (!super.equals(other)) return false if (!super.equals(other)) return false
other as FloatingLeg<*> other as FloatingLeg
if (floatingRatePayer != other.floatingRatePayer) return false if (floatingRatePayer != other.floatingRatePayer) return false
if (rollConvention != other.rollConvention) return false if (rollConvention != other.rollConvention) return false
@ -433,7 +428,7 @@ class InterestRateSwap : Contract {
index, indexSource, indexTenor) index, indexSource, indexTenor)
fun copy(floatingRatePayer: P = this.floatingRatePayer, fun copy(floatingRatePayer: AbstractParty = this.floatingRatePayer,
notional: Amount<Currency> = this.notional, notional: Amount<Currency> = this.notional,
paymentFrequency: Frequency = this.paymentFrequency, paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate, effectiveDate: LocalDate = this.effectiveDate,
@ -462,13 +457,6 @@ class InterestRateSwap : Contract {
paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment, rollConvention, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment, rollConvention,
fixingRollConvention, resetDayInMonth, fixingPeriod, resetRule, fixingsPerPayment, fixingRollConvention, resetDayInMonth, fixingPeriod, resetRule, fixingsPerPayment,
fixingCalendar, index, indexSource, indexTenor) fixingCalendar, index, indexSource, indexTenor)
fun toAnonymous(): FloatingLeg<AnonymousParty> {
return FloatingLeg(floatingRatePayer.toAnonymous(), notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, terminationDateAdjustment,
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment,
rollConvention, fixingRollConvention, resetDayInMonth, fixingPeriodOffset, resetRule, fixingsPerPayment,
fixingCalendar, index, indexSource, indexTenor)
}
} }
override fun verify(tx: TransactionForContract) = verifyClause(tx, AllOf(Clauses.Timestamped(), Clauses.Group()), tx.commands.select<Commands>()) override fun verify(tx: TransactionForContract) = verifyClause(tx, AllOf(Clauses.Timestamped(), Clauses.Group()), tx.commands.select<Commands>())
@ -478,7 +466,7 @@ class InterestRateSwap : Contract {
* Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides * Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides
* helper functions for the clauses. * helper functions for the clauses.
*/ */
abstract class AbstractIRSClause : Clause<State<*>, Commands, UniqueIdentifier>() { abstract class AbstractIRSClause : Clause<State, Commands, UniqueIdentifier>() {
// These functions may make more sense to use for basket types, but for now let's leave them here // These functions may make more sense to use for basket types, but for now let's leave them here
fun checkLegDates(legs: List<CommonLeg>) { fun checkLegDates(legs: List<CommonLeg>) {
requireThat { requireThat {
@ -494,7 +482,7 @@ class InterestRateSwap : Contract {
"The notional for all legs must be the same" using legs.all { it.notional == legs[0].notional } "The notional for all legs must be the same" using legs.all { it.notional == legs[0].notional }
} }
for (leg: CommonLeg in legs) { for (leg: CommonLeg in legs) {
if (leg is FixedLeg<*>) { if (leg is FixedLeg) {
requireThat { requireThat {
// TODO: Confirm: would someone really enter a swap with a negative fixed rate? // TODO: Confirm: would someone really enter a swap with a negative fixed rate?
"Fixed leg rate must be positive" using leg.fixedRate.isPositive() "Fixed leg rate must be positive" using leg.fixedRate.isPositive()
@ -520,9 +508,9 @@ class InterestRateSwap : Contract {
} }
} }
class Group : GroupClauseVerifier<State<*>, Commands, UniqueIdentifier>(AnyOf(Agree(), Fix(), Pay(), Mature())) { class Group : GroupClauseVerifier<State, Commands, UniqueIdentifier>(AnyOf(Agree(), Fix(), Pay(), Mature())) {
// Group by Trade ID for in / out states // Group by Trade ID for in / out states
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State<*>, UniqueIdentifier>> { override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, UniqueIdentifier>> {
return tx.groupStates { state -> state.linearId } return tx.groupStates { state -> state.linearId }
} }
} }
@ -543,12 +531,12 @@ class InterestRateSwap : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State<*>>, inputs: List<State>,
outputs: List<State<*>>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Agree>() val command = tx.commands.requireSingleCommand<Commands.Agree>()
val irs = outputs.filterIsInstance<State<*>>().single() val irs = outputs.filterIsInstance<State>().single()
requireThat { requireThat {
"There are no in states for an agreement" using inputs.isEmpty() "There are no in states for an agreement" using inputs.isEmpty()
"There are events in the fix schedule" using (irs.calculation.fixedLegPaymentSchedule.isNotEmpty()) "There are events in the fix schedule" using (irs.calculation.fixedLegPaymentSchedule.isNotEmpty())
@ -579,13 +567,13 @@ class InterestRateSwap : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State<*>>, inputs: List<State>,
outputs: List<State<*>>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Refix>() val command = tx.commands.requireSingleCommand<Commands.Refix>()
val irs = outputs.filterIsInstance<State<*>>().single() val irs = outputs.filterIsInstance<State>().single()
val prevIrs = inputs.filterIsInstance<State<*>>().single() val prevIrs = inputs.filterIsInstance<State>().single()
val paymentDifferences = getFloatingLegPaymentsDifferences(prevIrs.calculation.floatingLegPaymentSchedule, irs.calculation.floatingLegPaymentSchedule) val paymentDifferences = getFloatingLegPaymentsDifferences(prevIrs.calculation.floatingLegPaymentSchedule, irs.calculation.floatingLegPaymentSchedule)
// Having both of these tests are "redundant" as far as verify() goes, however, by performing both // Having both of these tests are "redundant" as far as verify() goes, however, by performing both
@ -624,8 +612,8 @@ class InterestRateSwap : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State<*>>, inputs: List<State>,
outputs: List<State<*>>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Pay>() val command = tx.commands.requireSingleCommand<Commands.Pay>()
@ -640,12 +628,12 @@ class InterestRateSwap : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java)
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<State<*>>, inputs: List<State>,
outputs: List<State<*>>, outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>, commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> { groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Mature>() val command = tx.commands.requireSingleCommand<Commands.Mature>()
val irs = inputs.filterIsInstance<State<*>>().single() val irs = inputs.filterIsInstance<State>().single()
requireThat { requireThat {
"No more fixings to be applied" using (irs.calculation.nextFixingDate() == null) "No more fixings to be applied" using (irs.calculation.nextFixingDate() == null)
"The irs is fully consumed and there is no id matched output state" using outputs.isEmpty() "The irs is fully consumed and there is no id matched output state" using outputs.isEmpty()
@ -667,9 +655,9 @@ class InterestRateSwap : Contract {
/** /**
* The state class contains the 4 major data classes. * The state class contains the 4 major data classes.
*/ */
data class State<P : AbstractParty>( data class State(
val fixedLeg: FixedLeg<P>, val fixedLeg: FixedLeg,
val floatingLeg: FloatingLeg<P>, val floatingLeg: FloatingLeg,
val calculation: Calculation, val calculation: Calculation,
val common: Common, val common: Common,
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID) override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
@ -682,15 +670,15 @@ class InterestRateSwap : Contract {
override val ref = common.tradeID override val ref = common.tradeID
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = parties.map { it.owningKey } get() = parties
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return fixedLeg.fixedRatePayer.owningKey.containsAny(ourKeys) || floatingLeg.floatingRatePayer.owningKey.containsAny(ourKeys) return fixedLeg.fixedRatePayer.owningKey.containsAny(ourKeys) || floatingLeg.floatingRatePayer.owningKey.containsAny(ourKeys)
} }
override val parties: List<AnonymousParty> override val parties: List<AbstractParty>
get() = listOf(fixedLeg.fixedRatePayer.toAnonymous(), floatingLeg.floatingRatePayer.toAnonymous()) get() = listOf(fixedLeg.fixedRatePayer, floatingLeg.floatingRatePayer)
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
val nextFixingOf = nextFixingOf() ?: return null val nextFixingOf = nextFixingOf() ?: return null
@ -700,10 +688,10 @@ class InterestRateSwap : Contract {
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant) return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant)
} }
override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg.toAnonymous(), fixedLeg.toAnonymous(), calculation, common, notary) override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary)
override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) { override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) {
InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this.toAnonymous(), oldState.state.notary), oldState.ref), fix) InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, oldState.state.notary), oldState.ref), fix)
} }
override fun nextFixingOf(): FixOf? { override fun nextFixingOf(): FixOf? {
@ -737,22 +725,13 @@ class InterestRateSwap : Contract {
* Just makes printing it out a bit better for those who don't have 80000 column wide monitors. * Just makes printing it out a bit better for those who don't have 80000 column wide monitors.
*/ */
fun prettyPrint() = toString().replace(",", "\n") fun prettyPrint() = toString().replace(",", "\n")
fun toAnonymous(): State<AnonymousParty> {
return if (this.fixedLeg.fixedRatePayer is AnonymousParty) {
@Suppress("UNCHECKED_CAST")
this as State<AnonymousParty>
} else {
State(fixedLeg.toAnonymous(), floatingLeg.toAnonymous(), calculation, common, linearId)
}
}
} }
/** /**
* This generates the agreement state and also the schedules from the initial data. * This generates the agreement state and also the schedules from the initial data.
* Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable. * Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable.
*/ */
fun generateAgreement(floatingLeg: FloatingLeg<AnonymousParty>, fixedLeg: FixedLeg<AnonymousParty>, calculation: Calculation, fun generateAgreement(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation,
common: Common, notary: Party): TransactionBuilder { common: Common, notary: Party): TransactionBuilder {
val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>() val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>()
@ -816,7 +795,7 @@ class InterestRateSwap : Contract {
} }
} }
fun generateFix(tx: TransactionBuilder, irs: StateAndRef<State<AnonymousParty>>, fixing: Fix) { fun generateFix(tx: TransactionBuilder, irs: StateAndRef<State>, fixing: Fix) {
tx.addInputState(irs) tx.addInputState(irs)
val fixedRate = FixedRate(RatioUnit(fixing.value)) val fixedRate = FixedRate(RatioUnit(fixing.value))
tx.addOutputState( tx.addOutputState(

View File

@ -1,6 +1,6 @@
package net.corda.irs.contract package net.corda.irs.contract
fun InterestRateSwap.State<*>.exportIRSToCSV(): String = fun InterestRateSwap.State.exportIRSToCSV(): String =
"Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" + "Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" +
this.calculation.fixedLegPaymentSchedule.toSortedMap().values.map { it.asCSV() }.joinToString("\n") + "\n" + this.calculation.fixedLegPaymentSchedule.toSortedMap().values.map { it.asCSV() }.joinToString("\n") + "\n" +
"Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" + "Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" +

View File

@ -79,9 +79,9 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
return future return future
} }
private fun loadLinearHeads(node: SimulatedNode): Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State<AnonymousParty>>> { private fun loadLinearHeads(node: SimulatedNode): Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State>> {
return node.database.transaction { return node.database.transaction {
node.services.vaultService.linearHeadsOfType<InterestRateSwap.State<AnonymousParty>>() node.services.vaultService.linearHeadsOfType<InterestRateSwap.State>()
} }
} }
@ -90,8 +90,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
val node1: SimulatedNode = banks[i] val node1: SimulatedNode = banks[i]
val node2: SimulatedNode = banks[j] val node2: SimulatedNode = banks[j]
val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State<AnonymousParty>>> = loadLinearHeads(node1) val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State>> = loadLinearHeads(node1)
val theDealRef: StateAndRef<InterestRateSwap.State<AnonymousParty>> = swaps.values.single() val theDealRef: StateAndRef<InterestRateSwap.State> = swaps.values.single()
// Do we have any more days left in this deal's lifetime? If not, return. // Do we have any more days left in this deal's lifetime? If not, return.
val nextFixingDate = theDealRef.state.data.calculation.nextFixingDate() ?: return null val nextFixingDate = theDealRef.state.data.calculation.nextFixingDate() ?: return null
@ -121,9 +121,9 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
// We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't // We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't
// have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable. // have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable.
// TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface. // TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface.
val irs = om.readValue<InterestRateSwap.State<AnonymousParty>>(javaClass.classLoader.getResource("simulation/trade.json")) val irs = om.readValue<InterestRateSwap.State>(javaClass.classLoader.getResource("simulation/trade.json"))
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity.toAnonymous() irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity.toAnonymous() irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity
@InitiatingFlow @InitiatingFlow
class StartDealFlow(val otherParty: Party, class StartDealFlow(val otherParty: Party,

View File

@ -16,12 +16,12 @@ import java.time.LocalDate
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> { fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
return when (irsSelect) { return when (irsSelect) {
1 -> { 1 -> {
val fixedLeg = InterestRateSwap.FixedLeg( val fixedLeg = InterestRateSwap.FixedLeg(
fixedRatePayer = MEGA_CORP.toAnonymous(), fixedRatePayer = MEGA_CORP,
notional = 15900000.DOLLARS, notional = 15900000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual, paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2016, 3, 10), effectiveDate = LocalDate.of(2016, 3, 10),
@ -40,7 +40,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
) )
val floatingLeg = InterestRateSwap.FloatingLeg( val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP.toAnonymous(), floatingRatePayer = MINI_CORP,
notional = 15900000.DOLLARS, notional = 15900000.DOLLARS,
paymentFrequency = Frequency.Quarterly, paymentFrequency = Frequency.Quarterly,
effectiveDate = LocalDate.of(2016, 3, 10), effectiveDate = LocalDate.of(2016, 3, 10),
@ -111,7 +111,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
// I did a mock up start date 10/03/2015 10/03/2025 so you have 5 cashflows on float side that have been preset the rest are unknown // I did a mock up start date 10/03/2015 10/03/2025 so you have 5 cashflows on float side that have been preset the rest are unknown
val fixedLeg = InterestRateSwap.FixedLeg( val fixedLeg = InterestRateSwap.FixedLeg(
fixedRatePayer = MEGA_CORP.toAnonymous(), fixedRatePayer = MEGA_CORP,
notional = 25000000.DOLLARS, notional = 25000000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual, paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2015, 3, 10), effectiveDate = LocalDate.of(2015, 3, 10),
@ -130,7 +130,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
) )
val floatingLeg = InterestRateSwap.FloatingLeg( val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP.toAnonymous(), floatingRatePayer = MINI_CORP,
notional = 25000000.DOLLARS, notional = 25000000.DOLLARS,
paymentFrequency = Frequency.Quarterly, paymentFrequency = Frequency.Quarterly,
effectiveDate = LocalDate.of(2015, 3, 10), effectiveDate = LocalDate.of(2015, 3, 10),
@ -246,8 +246,8 @@ class IRSTests {
/** /**
* Utility so I don't have to keep typing this. * Utility so I don't have to keep typing this.
*/ */
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State<AnonymousParty> { fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State {
return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State<AnonymousParty>>().single() return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single()
} }
/** /**
@ -301,7 +301,7 @@ class IRSTests {
var previousTXN = generateIRSTxn(1) var previousTXN = generateIRSTxn(1)
previousTXN.toLedgerTransaction(services).verify() previousTXN.toLedgerTransaction(services).verify()
services.recordTransactions(previousTXN) services.recordTransactions(previousTXN)
fun currentIRS() = previousTXN.tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State<AnonymousParty>>().single() fun currentIRS() = previousTXN.tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single()
while (true) { while (true) {
val nextFix: FixOf = currentIRS().nextFixingOf() ?: break val nextFix: FixOf = currentIRS().nextFixingOf() ?: break
@ -381,7 +381,7 @@ class IRSTests {
transaction("Fix") { transaction("Fix") {
input("irs post agreement") input("irs post agreement")
val postAgreement = "irs post agreement".output<InterestRateSwap.State<AnonymousParty>>() val postAgreement = "irs post agreement".output<InterestRateSwap.State>()
output("irs post first fixing") { output("irs post first fixing") {
postAgreement.copy( postAgreement.copy(
postAgreement.fixedLeg, postAgreement.fixedLeg,
@ -688,7 +688,7 @@ class IRSTests {
transaction("Fix") { transaction("Fix") {
input("irs post agreement1") input("irs post agreement1")
input("irs post agreement2") input("irs post agreement2")
val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State<AnonymousParty>>() val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State>()
output("irs post first fixing1") { output("irs post first fixing1") {
postAgreement1.copy( postAgreement1.copy(
postAgreement1.fixedLeg, postAgreement1.fixedLeg,
@ -697,7 +697,7 @@ class IRSTests {
postAgreement1.common.copy(tradeID = "t1") postAgreement1.common.copy(tradeID = "t1")
) )
} }
val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State<AnonymousParty>>() val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State>()
output("irs post first fixing2") { output("irs post first fixing2") {
postAgreement2.copy( postAgreement2.copy(
postAgreement2.fixedLeg, postAgreement2.fixedLeg,

View File

@ -13,6 +13,7 @@ import net.corda.core.crypto.generateKeyPair
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.LogHelper import net.corda.core.utilities.LogHelper
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
@ -241,5 +242,5 @@ class NodeInterestRatesTest {
} }
} }
private fun makeTX() = TransactionType.General.Builder(DUMMY_NOTARY).withItems(1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE_PUBKEY `with notary` DUMMY_NOTARY) private fun makeTX() = TransactionType.General.Builder(DUMMY_NOTARY).withItems(1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE `with notary` DUMMY_NOTARY)
} }

View File

@ -20,9 +20,8 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode:
} }
serviceHub.recordTransactions(issueTx.toSignedTransaction()) serviceHub.recordTransactions(issueTx.toSignedTransaction())
// Move ownership of the asset to the counterparty // Move ownership of the asset to the counterparty
val counterPartyKey = counterpartyNode.owningKey
val asset = issueTx.toWireTransaction().outRef<DummyContract.SingleOwnerState>(0) val asset = issueTx.toWireTransaction().outRef<DummyContract.SingleOwnerState>(0)
val moveTx = DummyContract.move(asset, counterPartyKey).apply { val moveTx = DummyContract.move(asset, counterpartyNode).apply {
signWith(myKeyPair) signWith(myKeyPair)
} }
// We don't check signatures because we know that the notary's signature is missing // We don't check signatures because we know that the notary's signature is missing

View File

@ -4,9 +4,9 @@ import net.corda.core.contracts.Command
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.crypto.keys import net.corda.core.crypto.keys
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey import java.security.PublicKey
@ -16,12 +16,12 @@ import java.security.PublicKey
* TODO: Merge with the existing demo IRS code. * TODO: Merge with the existing demo IRS code.
*/ */
data class IRSState(val swap: SwapData, data class IRSState(val swap: SwapData,
val buyer: AnonymousParty, val buyer: AbstractParty,
val seller: AnonymousParty, val seller: AbstractParty,
override val contract: OGTrade, override val contract: OGTrade,
override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState { override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState {
override val ref: String = linearId.externalId!! // Same as the constructor for UniqueIdentified override val ref: String = linearId.externalId!! // Same as the constructor for UniqueIdentified
override val parties: List<AnonymousParty> get() = listOf(buyer, seller) override val parties: List<AbstractParty> get() = listOf(buyer, seller)
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return parties.flatMap { it.owningKey.keys }.intersect(ourKeys).isNotEmpty() return parties.flatMap { it.owningKey.keys }.intersect(ourKeys).isNotEmpty()
@ -32,6 +32,6 @@ data class IRSState(val swap: SwapData,
return TransactionType.General.Builder(notary).withItems(state, Command(OGTrade.Commands.Agree(), parties.map { it.owningKey })) return TransactionType.General.Builder(notary).withItems(state, Command(OGTrade.Commands.Agree(), parties.map { it.owningKey }))
} }
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = parties.map { it.owningKey } get() = parties
} }

View File

@ -45,7 +45,7 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
require(inputs.size == 0) { "Inputs must be empty" } require(inputs.size == 0) { "Inputs must be empty" }
require(outputs.size == 1) { "" } require(outputs.size == 1) { "" }
require(outputs[0].buyer != outputs[0].seller) require(outputs[0].buyer != outputs[0].seller)
require(outputs[0].parties.map { it.owningKey }.containsAll(outputs[0].participants)) require(outputs[0].parties.containsAll(outputs[0].participants))
require(outputs[0].parties.containsAll(listOf(outputs[0].buyer, outputs[0].seller))) require(outputs[0].parties.containsAll(listOf(outputs[0].buyer, outputs[0].seller)))
require(outputs[0].swap.startDate.isBefore(outputs[0].swap.endDate)) require(outputs[0].swap.startDate.isBefore(outputs[0].swap.endDate))
require(outputs[0].swap.notional > BigDecimal(0)) require(outputs[0].swap.notional > BigDecimal(0))

View File

@ -1,10 +1,10 @@
package net.corda.vega.contracts package net.corda.vega.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.crypto.keys import net.corda.core.crypto.keys
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.vega.flows.SimmRevaluation import net.corda.vega.flows.SimmRevaluation
@ -19,7 +19,7 @@ import java.time.temporal.ChronoUnit
*/ */
data class PortfolioState(val portfolio: List<StateRef>, data class PortfolioState(val portfolio: List<StateRef>,
override val contract: PortfolioSwap, override val contract: PortfolioSwap,
private val _parties: Pair<AnonymousParty, AnonymousParty>, private val _parties: Pair<AbstractParty, AbstractParty>,
val valuationDate: LocalDate, val valuationDate: LocalDate,
val valuation: PortfolioValuation? = null, val valuation: PortfolioValuation? = null,
override val linearId: UniqueIdentifier = UniqueIdentifier()) override val linearId: UniqueIdentifier = UniqueIdentifier())
@ -27,12 +27,12 @@ data class PortfolioState(val portfolio: List<StateRef>,
@CordaSerializable @CordaSerializable
data class Update(val portfolio: List<StateRef>? = null, val valuation: PortfolioValuation? = null) data class Update(val portfolio: List<StateRef>? = null, val valuation: PortfolioValuation? = null)
override val parties: List<AnonymousParty> get() = _parties.toList() override val parties: List<AbstractParty> get() = _parties.toList()
override val ref: String = linearId.toString() override val ref: String = linearId.toString()
val valuer: AnonymousParty get() = parties[0] val valuer: AbstractParty get() = parties[0]
override val participants: List<PublicKey> override val participants: List<AbstractParty>
get() = parties.map { it.owningKey } get() = parties
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity {
val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now()) val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now())

View File

@ -71,7 +71,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
"there are no inputs" using (inputs.size == 0) "there are no inputs" using (inputs.size == 0)
"there is one output" using (outputs.size == 1) "there is one output" using (outputs.size == 1)
"valuer must be a party" using (outputs[0].parties.contains(outputs[0].valuer)) "valuer must be a party" using (outputs[0].parties.contains(outputs[0].valuer))
"all participants must be parties" using (outputs[0].parties.map { it.owningKey }.containsAll(outputs[0].participants)) "all participants must be parties" using (outputs[0].parties.containsAll(outputs[0].participants))
} }
return setOf(command.value) return setOf(command.value)

View File

@ -1,10 +1,10 @@
package net.corda.vega.flows package net.corda.vega.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.identity.Party
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -38,7 +38,7 @@ object IRSTradeFlow {
} else { } else {
Pair(otherParty, myIdentity) Pair(otherParty, myIdentity)
} }
val offer = IRSState(swap, buyer.toAnonymous(), seller.toAnonymous(), OGTrade()) val offer = IRSState(swap, buyer, seller, OGTrade())
logger.info("Handshake finished, sending IRS trade offer message") logger.info("Handshake finished, sending IRS trade offer message")
val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it } val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it }

View File

@ -10,11 +10,11 @@ import com.opengamma.strata.pricer.rate.ImmutableRatesProvider
import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.services.dealsWith import net.corda.core.node.services.dealsWith
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -85,7 +85,7 @@ object SimmFlow {
@Suspendable @Suspendable
private fun agreePortfolio(portfolio: Portfolio) { private fun agreePortfolio(portfolio: Portfolio) {
logger.info("Agreeing portfolio") logger.info("Agreeing portfolio")
val parties = Pair(myIdentity.toAnonymous(), otherParty.toAnonymous()) val parties = Pair(myIdentity, otherParty)
val portfolioState = PortfolioState(portfolio.refs, PortfolioSwap(), parties, valuationDate) val portfolioState = PortfolioState(portfolio.refs, PortfolioSwap(), parties, valuationDate)
send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate)) send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate))
@ -219,13 +219,6 @@ object SimmFlow {
return receive<Boolean>(replyToParty).unwrap { it } return receive<Boolean>(replyToParty).unwrap { it }
} }
@Suspendable
private fun agreeValuation(portfolio: Portfolio, asOf: LocalDate, valuer: AnonymousParty): PortfolioValuation {
val valuerParty = serviceHub.identityService.partyFromAnonymous(valuer)
require(valuerParty != null)
return agreeValuation(portfolio, asOf, valuerParty!!)
}
/** /**
* So this is the crux of the Simm Agreement flow * So this is the crux of the Simm Agreement flow
* It needs to do several things - which are mainly defined by the analytics engine we are using - which in this * It needs to do several things - which are mainly defined by the analytics engine we are using - which in this
@ -326,7 +319,7 @@ object SimmFlow {
@Suspendable @Suspendable
private fun updateValuation(stateRef: StateAndRef<PortfolioState>) { private fun updateValuation(stateRef: StateAndRef<PortfolioState>) {
val portfolio = stateRef.state.data.portfolio.toStateAndRef<IRSState>(serviceHub).toPortfolio() val portfolio = stateRef.state.data.portfolio.toStateAndRef<IRSState>(serviceHub).toPortfolio()
val valuer = stateRef.state.data.valuer val valuer = serviceHub.identityService.partyFromAnonymous(stateRef.state.data.valuer) ?: throw IllegalStateException("Unknown valuer party ${stateRef.state.data.valuer}")
val valuation = agreeValuation(portfolio, offer.valuationDate, valuer) val valuation = agreeValuation(portfolio, offer.valuationDate, valuer)
subFlow(object : StateRevisionFlow.Receiver<PortfolioState.Update>(replyToParty) { subFlow(object : StateRevisionFlow.Receiver<PortfolioState.Update>(replyToParty) {
override fun verifyProposal(proposal: Proposal<PortfolioState.Update>) { override fun verifyProposal(proposal: Proposal<PortfolioState.Update>) {

View File

@ -21,7 +21,7 @@ object SimmRevaluation {
override fun call(): Unit { override fun call(): Unit {
val stateAndRef = serviceHub.vaultService.linearHeadsOfType<PortfolioState>().values.first { it.ref == curStateRef } val stateAndRef = serviceHub.vaultService.linearHeadsOfType<PortfolioState>().values.first { it.ref == curStateRef }
val curState = stateAndRef.state.data val curState = stateAndRef.state.data
val myIdentity = serviceHub.myInfo.legalIdentity.toAnonymous() val myIdentity = serviceHub.myInfo.legalIdentity
if (myIdentity == curState.parties[0]) { if (myIdentity == curState.parties[0]) {
val otherParty = serviceHub.identityService.partyFromAnonymous(curState.parties[1]) val otherParty = serviceHub.identityService.partyFromAnonymous(curState.parties[1])
require(otherParty != null) { "Other party must be known by this node" } require(otherParty != null) { "Other party must be known by this node" }

View File

@ -1,6 +1,7 @@
package net.corda.vega.flows package net.corda.vega.flows
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -16,7 +17,7 @@ import java.security.PublicKey
object StateRevisionFlow { object StateRevisionFlow {
class Requester<T>(curStateRef: StateAndRef<RevisionedState<T>>, class Requester<T>(curStateRef: StateAndRef<RevisionedState<T>>,
updatedData: T) : AbstractStateReplacementFlow.Instigator<RevisionedState<T>, RevisionedState<T>, T>(curStateRef, updatedData) { updatedData: T) : AbstractStateReplacementFlow.Instigator<RevisionedState<T>, RevisionedState<T>, T>(curStateRef, updatedData) {
override fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> { override fun assembleTx(): Pair<SignedTransaction, List<AbstractParty>> {
val state = originalState.state.data val state = originalState.state.data
val tx = state.generateRevision(originalState.state.notary, originalState, modification) val tx = state.generateRevision(originalState.state.notary, originalState, modification)
tx.setTime(serviceHub.clock.instant(), 30.seconds) tx.setTime(serviceHub.clock.instant(), 30.seconds)

Some files were not shown because too many files have changed in this diff Show More