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.BusinessCalendar
import 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.messaging.CordaRPCOps
@ -66,6 +67,7 @@ object JacksonSupport {
addDeserializer(AnonymousParty::class.java, AnonymousPartyDeserializer)
addSerializer(Party::class.java, PartySerializer)
addDeserializer(Party::class.java, PartyDeserializer)
addDeserializer(AbstractParty::class.java, PartyDeserializer)
addSerializer(BigDecimal::class.java, ToStringSerializer)
addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer())
addSerializer(SecureHash::class.java, SecureHashSerializer)
@ -160,8 +162,16 @@ object JacksonSupport {
}
val mapper = parser.codec as PartyObjectMapper
val principal = X500Name(parser.text)
return mapper.partyFromPrincipal(principal) ?: throw JsonParseException(parser, "Could not find a Party with name ${principal}")
// 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)
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
// see a signature from each of those keys. The actual signatures have been verified against the transaction
// 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 keysThatSigned = command.signers.toSet()
requireThat {

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
// 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
}
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 participants: List<PublicKey>
override val participants: List<AbstractParty>
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.
*/
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 participants: List<PublicKey> get() = owners
override val participants: List<AbstractParty> get() = owners
}
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 {
val owners = listOf(owner) + otherOwners
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))
} 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 }))
}
}
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder {
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: AbstractParty) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: AbstractParty): TransactionBuilder {
require(priors.isNotEmpty())
val priorState = priors[0].state.data
val (cmd, state) = priorState.withNewOwner(newOwner)
return TransactionType.General.Builder(notary = priors[0].state.notary).withItems(
/* INPUTS */ *priors.toTypedArray(),
/* COMMAND */ Command(cmd, priorState.owner),
/* COMMAND */ Command(cmd, priorState.owner.owningKey),
/* OUTPUT */ state
)
}

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.WireTransaction
import net.corda.flows.ContractUpgradeFlow
import java.security.PublicKey
// The dummy contract doesn't do anything useful. It exists for testing purposes.
val DUMMY_V2_PROGRAM_ID = DummyContractV2()
@ -15,9 +15,9 @@ val DUMMY_V2_PROGRAM_ID = DummyContractV2()
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
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 participants: List<PublicKey> = owners
override val participants: List<AbstractParty> = owners
}
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.
*/
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()
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 {
states.forEach {
addInputState(it)
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)
}

View File

@ -1,12 +1,12 @@
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].
*/
data class DummyState(val magicNumber: Int = 0) : ContractState {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey>
override val participants: List<AbstractParty>
get() = emptyList()
}

View File

@ -1,12 +1,8 @@
package net.corda.core.contracts
import net.corda.core.identity.Party
import net.corda.core.flows.FlowException
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
import net.corda.core.identity.AbstractParty
import java.security.PublicKey
import java.util.*
class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing")
@ -32,9 +28,9 @@ interface FungibleAsset<T : Any> : OwnableState {
*/
val exitKeys: Collection<PublicKey>
/** 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
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.flows.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
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
* 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 {
/** 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 */
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 */
@ -280,7 +281,7 @@ interface DealState : LinearState {
* separate process exchange certificates to ascertain identities. Thus decoupling identities from
* [ContractState]s.
* */
val parties: List<AnonymousParty>
val parties: List<AbstractParty>
/**
* 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.
*/
@CordaSerializable
data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) {
constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), reference)
data class PartyAndReference(val party: AbstractParty, val reference: OpaqueBytes) {
override fun toString() = "$party$reference"
}

View File

@ -148,7 +148,7 @@ sealed class TransactionType {
*/
class Builder(notary: Party) : TransactionBuilder(NotaryChange, notary) {
override fun addInputState(stateAndRef: StateAndRef<*>) {
signers.addAll(stateAndRef.state.data.participants)
signers.addAll(stateAndRef.state.data.participants.map { it.owningKey })
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
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAPublicKey
import java.math.BigInteger
import java.security.*
@ -18,6 +19,8 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun toString() = "NULL_KEY"
}
val NULL_PARTY = AnonymousParty(NullPublicKey)
// TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable
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 hashCode(): Int = owningKey.hashCode()
abstract fun toAnonymous(): AnonymousParty
abstract fun nameOrNull(): X500Name?
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 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
open class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
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 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
import net.corda.core.contracts.PartyAndReference
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import org.bouncycastle.asn1.x500.X500Name
@ -54,7 +55,7 @@ interface IdentityService {
fun partyFromName(name: String): Party?
fun partyFromX500Name(principal: X500Name): Party?
fun partyFromAnonymous(party: AnonymousParty): Party?
fun partyFromAnonymous(party: AbstractParty): 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 com.google.common.util.concurrent.ListenableFuture
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.identity.AbstractParty
import net.corda.core.identity.Party
@ -286,7 +288,7 @@ interface VaultService {
@Suspendable
fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>,
to: PublicKey,
to: AbstractParty,
onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<PublicKey>>
// DOCSTART VaultStatesQuery

View File

@ -7,6 +7,7 @@ import net.corda.core.contracts.StateRef
import net.corda.core.crypto.*
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
@ -59,13 +60,13 @@ abstract class AbstractStateReplacementFlow {
progressTracker.currentStep = SIGNING
val myKey = serviceHub.myInfo.legalIdentity.owningKey
val myKey = serviceHub.myInfo.legalIdentity
val me = listOf(myKey)
val signatures = if (participants == me) {
getNotarySignatures(stx)
} else {
collectSignatures(participants - me, stx)
collectSignatures((participants - me).map { it.owningKey }, stx)
}
val finalTx = stx + signatures
@ -73,7 +74,7 @@ abstract class AbstractStateReplacementFlow {
return finalTx.tx.outRef(0)
}
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>>
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>>
@Suspendable
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.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
@ -32,12 +33,12 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
@JvmStatic
fun verify(input: ContractState, output: ContractState, commandData: Command) {
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()
@Suppress("UNCHECKED_CAST")
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *>
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)
"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))
@ -53,11 +54,11 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
.withItems(
stateRef,
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)
.signWith(serviceHub.legalIdentityKey)
.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.
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?
val parties = keys.mapNotNull { serviceHub.identityService.partyFromKey(it) }.toSet()
val parties = keys.mapNotNull { serviceHub.identityService.partyFromAnonymous(it) }.toSet()
Pair(stx, parties)
}
}

View File

@ -2,6 +2,7 @@ package net.corda.flows
import net.corda.core.contracts.*
import net.corda.core.flows.InitiatingFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
@ -24,11 +25,11 @@ class NotaryChangeFlow<out T : ContractState>(
progressTracker: ProgressTracker = tracker())
: 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 tx = TransactionType.NotaryChange.Builder(originalState.state.notary)
val participants: Iterable<PublicKey>
val participants: Iterable<AbstractParty>
if (state.encumbrance == null) {
val modifiedState = TransactionState(state.data, modification)
@ -53,14 +54,14 @@ class NotaryChangeFlow<out T : ContractState>(
*
* @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 txId = stateRef.txhash
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId)
?: throw StateReplacementException("Transaction $txId not found")
val outputs = issuingTx.tx.outputs
val participants = mutableSetOf<PublicKey>()
val participants = mutableSetOf<AbstractParty>()
var nextStateIndex = stateRef.index
var newOutputPosition = tx.outputStates().size

View File

@ -1,6 +1,7 @@
package net.corda.core.contracts
import net.corda.core.crypto.SecureHash
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.ALICE_PUBKEY
import org.junit.Test
@ -14,7 +15,7 @@ class DummyContractV2Tests {
@Test
fun `upgrade from v1`() {
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 v1StateAndRef = StateAndRef(v1State, v1Ref)
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.core.crypto.SecureHash
import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.core.utilities.DUMMY_PUBKEY_2
import net.corda.core.identity.AbstractParty
import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP
import net.corda.testing.ledger
import net.corda.testing.transaction
import org.junit.Test
import java.security.PublicKey
import java.time.Instant
import java.time.temporal.ChronoUnit
@ -19,9 +18,9 @@ class TransactionEncumbranceTests {
val state = Cash.State(
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 FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS)
@ -40,7 +39,7 @@ class TransactionEncumbranceTests {
data class State(
val validFrom: Instant
) : ContractState {
override val participants: List<PublicKey> = emptyList()
override val participants: List<AbstractParty> = emptyList()
override val contract: Contract = TEST_TIMELOCK_ID
}
}
@ -52,7 +51,7 @@ class TransactionEncumbranceTests {
input { state }
output(encumbrance = 1) { stateWithNewOwner }
output("5pm time-lock") { timeLock }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
verifies()
}
}
@ -70,7 +69,7 @@ class TransactionEncumbranceTests {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FIVE_PM)
verifies()
}
@ -89,7 +88,7 @@ class TransactionEncumbranceTests {
input("state encumbered by 5pm time-lock")
input("5pm time-lock")
output { state }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FOUR_PM)
this `fails with` "the time specified in the time-lock has passed"
}
@ -106,7 +105,7 @@ class TransactionEncumbranceTests {
transaction {
input("state encumbered by 5pm time-lock")
output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FIVE_PM)
this `fails with` "Missing required encumbrance 1 in INPUT"
}
@ -118,7 +117,7 @@ class TransactionEncumbranceTests {
transaction {
input { state }
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"
}
}
@ -129,7 +128,7 @@ class TransactionEncumbranceTests {
input { state }
output(encumbrance = 2) { stateWithNewOwner }
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"
}
}
@ -146,7 +145,7 @@ class TransactionEncumbranceTests {
input("state encumbered by some other state")
input("5pm time-lock")
output { stateWithNewOwner }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
timestamp(FIVE_PM)
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.WireTransaction
import net.corda.core.utilities.*
import net.corda.testing.ALICE_PUBKEY
import org.junit.Test
import java.security.KeyPair
import kotlin.test.assertEquals
@ -95,7 +94,7 @@ class TransactionTests {
@Test
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 outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB))
val commands = emptyList<AuthenticatedObject<CommandData>>()
@ -120,7 +119,7 @@ class TransactionTests {
@Test
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 stateAndRef = StateAndRef(baseOutState, stateRef)
val inputs = listOf(stateAndRef, stateAndRef)
@ -148,7 +147,7 @@ class TransactionTests {
@Test
fun `general transactions cannot change 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 inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0)))
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_PUBKEY_1
import net.corda.core.utilities.TEST_TX_TIME
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY
import net.corda.testing.ledger
import net.corda.testing.*
import org.junit.Test
import java.security.PublicKey
import kotlin.test.*
@ -30,20 +28,20 @@ class PartialMerkleTreeTest {
output("MEGA_CORP cash") {
Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP_PUBKEY
owner = MEGA_CORP
)
}
output("dummy cash 1") {
Cash.State(
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = DUMMY_PUBKEY_1
owner = MINI_CORP
)
}
}
transaction {
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() }
timestamp(TEST_TX_TIME)
this.verifies()
@ -98,7 +96,7 @@ class PartialMerkleTreeTest {
fun filtering(elem: Any): Boolean {
return when (elem) {
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 Timestamp -> true
is PublicKey -> elem == MEGA_CORP_PUBKEY

View File

@ -88,7 +88,7 @@ class CollectSignaturesFlowTests {
val state = receive<DummyContract.MultiOwnerState>(otherParty).unwrap { it }
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 ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
val stx = subFlow(CollectSignaturesFlow(ptx))
@ -108,7 +108,7 @@ class CollectSignaturesFlowTests {
@Suspendable
override fun call(): SignedTransaction {
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 ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
val stx = subFlow(CollectSignaturesFlow(ptx))
@ -142,7 +142,7 @@ class CollectSignaturesFlowTests {
fun `successfully collects two signatures`() {
val magicNumber = 1337
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))
mockNet.runNetwork()
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.crypto.SecureHash
import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow
@ -27,7 +28,6 @@ import net.corda.testing.startRpcClient
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.security.PublicKey
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@ -187,21 +187,21 @@ class ContractUpgradeFlowTest {
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.")
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> {
override val legacyContract = Cash::class.java
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<PublicKey>) : FungibleAsset<Currency> {
override val owner: PublicKey = owners.first()
override val exitKeys = (owners + amount.token.issuer.party.owningKey).toSet()
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
override val owner: AbstractParty = owners.first()
override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet()
override val contract = CashV2()
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 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))

View File

@ -12,7 +12,7 @@ import net.corda.flows.ResolveTransactionsFlow
import net.corda.node.utilities.transaction
import net.corda.testing.MEGA_CORP
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 org.junit.After
import org.junit.Before
@ -94,7 +94,7 @@ class ResolveTransactionsFlowTest {
val count = 50
var cursor = stx2
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)
.toSignedTransaction(false)
a.database.transaction {
@ -113,13 +113,13 @@ class ResolveTransactionsFlowTest {
fun `triangle of transactions resolves fine`() {
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(DUMMY_NOTARY_KEY)
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(DUMMY_NOTARY_KEY)
toSignedTransaction()
@ -173,7 +173,7 @@ class ResolveTransactionsFlowTest {
it.signWith(DUMMY_NOTARY_KEY)
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(DUMMY_NOTARY_KEY)
it.toSignedTransaction()

View File

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

View File

@ -2,10 +2,10 @@ package net.corda.core.node
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.node.services.Vault
import net.corda.core.utilities.DUMMY_NOTARY
import org.junit.Test
import java.security.PublicKey
import kotlin.test.assertEquals
@ -20,7 +20,7 @@ class VaultUpdateTests {
}
private class DummyState : ContractState {
override val participants: List<PublicKey>
override val participants: List<AbstractParty>
get() = emptyList()
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.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.seconds
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.generateStateRef
import org.junit.Before
import org.junit.Test
import java.security.PublicKey
import java.security.SignatureException
import java.util.*
import kotlin.test.assertEquals
@ -27,12 +32,12 @@ class TransactionSerializationTests {
data class State(
val deposit: PartyAndReference,
val amount: Amount<Currency>,
override val owner: PublicKey) : OwnableState {
override val owner: AbstractParty) : OwnableState {
override val contract: Contract = TEST_PROGRAM_ID
override val participants: List<PublicKey>
override val participants: List<AbstractParty>
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 {
@ -44,9 +49,9 @@ class TransactionSerializationTests {
// It refers to a fake TX/state that we don't bother creating here.
val depositRef = MINI_CORP.ref(1)
val fakeStateRef = generateStateRef()
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY), fakeStateRef)
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY)
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public), DUMMY_NOTARY)
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), DUMMY_NOTARY), fakeStateRef)
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY)
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY)
lateinit var tx: TransactionBuilder
@ -54,14 +59,14 @@ class TransactionSerializationTests {
@Before
fun setup() {
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
fun signWireTX() {
tx.signWith(DUMMY_NOTARY_KEY)
tx.signWith(DUMMY_KEY_1)
tx.signWith(MEGA_CORP_KEY)
val signedTX = tx.toSignedTransaction()
// Now check that the signature we just made verifies.
@ -81,7 +86,7 @@ class TransactionSerializationTests {
tx.toSignedTransaction()
}
tx.signWith(DUMMY_KEY_1)
tx.signWith(MEGA_CORP_KEY)
tx.signWith(DUMMY_NOTARY_KEY)
val signedTX = tx.toSignedTransaction()
@ -104,7 +109,7 @@ class TransactionSerializationTests {
@Test
fun timestamp() {
tx.setTime(TEST_TX_TIME, 30.seconds)
tx.signWith(DUMMY_KEY_1)
tx.signWith(MEGA_CORP_KEY)
tx.signWith(DUMMY_NOTARY_KEY)
val stx = tx.toSignedTransaction()
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
backwards compatibility, but it will be removed in a future release and developers should move to the new class as soon
as possible.
* There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. A new class
``AnonymousParty`` has been added, which is intended to be used in place of ``Party`` or ``PublicKey`` in contract
state objects. The exception to this is where the party in a contract state is intended to be well known, such as
issuer of a ``Cash`` state.
* There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. This now replaces
use of ``Party`` and ``PublicKey`` in state objects, and allows use of full or anonymised parties depending on
use-case.
* 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.

View File

@ -48,12 +48,12 @@ private fun gatherOurInputs(serviceHub: ServiceHub,
notary: Party?): Pair<List<StateAndRef<Cash.State>>, Long> {
// Collect cash type inputs
val cashStates = serviceHub.vaultService.unconsumedStates<Cash.State>()
// extract our key identity for convenience
val ourKey = serviceHub.myInfo.legalIdentity.owningKey
// extract our identity for convenience
val ourIdentity = serviceHub.myInfo.legalIdentity
// Filter down to our own cash states with right currency and issuer
val suitableCashStates = cashStates.filter {
val state = it.state.data
(state.owner == ourKey)
(state.owner == ourIdentity)
&& (state.amount.token == amountRequired.token)
}
require(!suitableCashStates.isEmpty()) { "Insufficient funds" }
@ -90,12 +90,12 @@ private fun prepareOurInputsAndOutputs(serviceHub: ServiceHub, request: FxReques
val (inputs, residual) = gatherOurInputs(serviceHub, sellAmount, request.notary)
// 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) {
// Build an output state for the residual change back to us
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))
} else {
return FxResponse(inputs, listOf(transferedFundsOutput))
@ -140,7 +140,7 @@ class ForeignExchangeFlow(val tradeId: String,
require(it.inputs.all { it.state.notary == notary }) {
"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"
}
require(it.inputs.all { it.state.data.amount.token == remoteRequestWithNotary.amount.token }) {
@ -153,7 +153,7 @@ class ForeignExchangeFlow(val tradeId: String,
>= remoteRequestWithNotary.amount.quantity) {
"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) {
"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)
// 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 theirSigners = theirStates.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.owningKey }.toSet()
builder.addCommand(Cash.Commands.Move(), (ourSigners + theirSigners).toList())
// Build and add the inputs and outputs

View File

@ -2,9 +2,13 @@ package net.corda.docs
import co.paralleluniverse.fibers.Suspendable
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.InitiatingFlow
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub
@ -64,10 +68,10 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState {
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 {
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
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

View File

@ -2,16 +2,16 @@ package net.corda.contracts.universal
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder
import java.math.BigDecimal
import java.security.PublicKey
import java.time.Instant
val UNIVERSAL_PROGRAM_ID = UniversalContract()
class UniversalContract : Contract {
data class State(override val participants: List<PublicKey>,
data class State(override val participants: List<AbstractParty>,
val details: Arrangement) : ContractState {
override val contract = UNIVERSAL_PROGRAM_ID
}
@ -316,7 +316,7 @@ class UniversalContract : Contract {
override val legalContractReference: SecureHash
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())
tx.addOutputState(State(listOf(notary), arrangement))
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 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 statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst)
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterExecutionFirst)
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFirst)
val stateAfterFixingFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFinal)
val statePaymentFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFinal)
val stateAfterFixingFinal = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFinal)
val statePaymentFinal = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFinal)
val contractLimitedCap = arrange {
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 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
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_AFTER_MATURITY: Instant get() = Instant.parse("2018-06-02T12:00:00.00Z")
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), initialContract)
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract1)
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract2)
val inState = UniversalContract.State(listOf(DUMMY_NOTARY), initialContract)
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY), outContract1)
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY), outContract2)
@Test
fun `issue - signature`() {

View File

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

View File

@ -122,12 +122,12 @@ class IRS {
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 stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterExecutionFirst)
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFirst)
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

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 {
rollOut("2016-10-03".ld, "2017-09-01".ld, Frequency.Monthly) {
@ -55,8 +55,8 @@ class RollOutTests {
highStreetBank.owes(acmeCorp, 10.K, USD)
}
val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1a)
val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1b)
val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY), contractStep1a)
val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY), contractStep1b)
val contract_transfer1 = arrange {
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
fun issue() {

View File

@ -33,12 +33,12 @@ class ZeroCouponBond {
val transfer = arrange { highStreetBank.owes(acmeCorp, 100.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 outStateWrong = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferWrong)
val outState = UniversalContract.State(listOf(DUMMY_NOTARY), transfer)
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
fun basic() {

View File

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

View File

@ -1,6 +1,7 @@
package net.corda.contracts;
import net.corda.core.contracts.*;
import net.corda.core.identity.AbstractParty;
import java.security.PublicKey;
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).
*/
public interface ICommercialPaperState extends ContractState {
ICommercialPaperState withOwner(PublicKey newOwner);
ICommercialPaperState withOwner(AbstractParty newOwner);
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.clauses.*;
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.node.services.*;
import net.corda.core.transactions.*;
@ -34,14 +37,14 @@ public class JavaCommercialPaper implements Contract {
@SuppressWarnings("unused")
public static class State implements OwnableState, ICommercialPaperState {
private PartyAndReference issuance;
private PublicKey owner;
private AbstractParty owner;
private Amount<Issued<Currency>> faceValue;
private Instant maturityDate;
public State() {
} // For serialization
public State(PartyAndReference issuance, PublicKey owner, Amount<Issued<Currency>> faceValue,
public State(PartyAndReference issuance, AbstractParty owner, Amount<Issued<Currency>> faceValue,
Instant maturityDate) {
this.issuance = issuance;
this.owner = owner;
@ -53,13 +56,13 @@ public class JavaCommercialPaper implements Contract {
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);
}
@NotNull
@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));
}
@ -76,7 +79,7 @@ public class JavaCommercialPaper implements Contract {
}
@NotNull
public PublicKey getOwner() {
public AbstractParty getOwner() {
return owner;
}
@ -117,12 +120,12 @@ public class JavaCommercialPaper implements Contract {
}
public State withoutOwner() {
return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate);
return new State(issuance, new AnonymousParty(NullPublicKey.INSTANCE), faceValue, maturityDate);
}
@NotNull
@Override
public List<PublicKey> getParticipants() {
public List<AbstractParty> getParticipants() {
return ImmutableList.of(this.owner);
}
}
@ -162,12 +165,12 @@ public class JavaCommercialPaper implements Contract {
@NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
@NotNull State groupingKey) {
State groupingKey) {
AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
// There should be only a single input due to aggregation above
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");
// 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> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
@NotNull State groupingKey) {
State groupingKey) {
AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class);
// There should be only a single input due to aggregation above
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");
Timestamp timestamp = tx.getTimestamp();
@ -237,7 +240,7 @@ public class JavaCommercialPaper implements Contract {
@NotNull List<? extends State> inputs,
@NotNull List<? extends State> outputs,
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
@NotNull State groupingKey) {
State groupingKey) {
AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class);
State output = single(outputs);
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) {
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);
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 {
vault.generateSpend(tx, StructuresKt.withoutIssuer(paper.getState().getData().getFaceValue()), paper.getState().getData().getOwner(), null);
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.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.crypto.SecureHash
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService
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.utilities.Emoji
import net.corda.schemas.CommercialPaperSchemaV1
import java.security.PublicKey
import java.time.Instant
import java.util.*
@ -61,22 +61,22 @@ class CommercialPaper : Contract {
data class State(
val issuance: PartyAndReference,
override val owner: PublicKey,
override val owner: AbstractParty,
val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant
) : OwnableState, QueryableState, ICommercialPaperState {
override val contract = CP_PROGRAM_ID
override val participants: List<PublicKey>
override val participants: List<AbstractParty>
get() = listOf(owner)
val token: Issued<Terms>
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)"
// 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 withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
@ -91,7 +91,7 @@ class CommercialPaper : Contract {
is CommercialPaperSchemaV1 -> CommercialPaperSchemaV1.PersistentCommercialPaperState(
issuanceParty = this.issuance.party.owningKey.toBase58String(),
issuanceRef = this.issuance.reference.bytes,
owner = this.owner.toBase58String(),
owner = this.owner.owningKey.toBase58String(),
maturity = this.maturityDate,
faceValue = this.faceValue.quantity,
currency = this.faceValue.token.product.currencyCode,
@ -146,7 +146,7 @@ class CommercialPaper : Contract {
val command = commands.requireSingleCommand<Commands.Move>()
val input = inputs.single()
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)
// 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.
@ -175,7 +175,7 @@ class CommercialPaper : Contract {
"the paper must have matured" using (time >= input.maturityDate)
"the received amount equals the face value" using (received == input.faceValue)
"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)
@ -196,17 +196,17 @@ class CommercialPaper : Contract {
* 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 {
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))
}
/**
* 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.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) }
vault.generateSpend(tx, amount, paper.state.data.owner)
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 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 net.corda.contracts.asset.sumCashBy
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.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji
import java.security.PublicKey
import java.time.Instant
import java.util.*
@ -27,19 +27,19 @@ class CommercialPaperLegacy : Contract {
data class State(
val issuance: PartyAndReference,
override val owner: PublicKey,
override val owner: AbstractParty,
val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant
) : OwnableState, ICommercialPaperState {
override val contract = CP_LEGACY_PROGRAM_ID
override val participants = listOf(owner)
fun withoutOwner() = copy(owner = NullPublicKey)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
fun withoutOwner() = copy(owner = NULL_PARTY)
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)"
// 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 withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
@ -70,7 +70,7 @@ class CommercialPaperLegacy : Contract {
is Commands.Move -> {
val input = inputs.single()
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)
// 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.
@ -86,7 +86,7 @@ class CommercialPaperLegacy : Contract {
"the paper must have matured" using (time >= input.maturityDate)
"the received amount equals the face value" using (received == input.faceValue)
"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,
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))
}
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: AbstractParty) {
tx.addInputState(paper)
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)
@ -130,6 +130,6 @@ class CommercialPaperLegacy : Contract {
// Add the cash movement using the states in our vault.
vault.generateSpend(tx, paper.state.data.faceValue.withoutIssuer(), paper.state.data.owner)
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 org.bouncycastle.asn1.x500.X500Name
import java.math.BigInteger
import java.security.PublicKey
import java.util.*
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -87,27 +86,27 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
override val amount: Amount<Issued<Currency>>,
/** There must be a MoveCommand signed by this key to claim the amount. */
override val owner: PublicKey
override val owner: AbstractParty
) : 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)
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 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)
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. */
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is CashSchemaV1 -> CashSchemaV1.PersistentCashState(
owner = this.owner.toBase58String(),
owner = this.owner.owningKey.toBase58String(),
pennies = this.amount.quantity,
currency = this.amount.token.product.currencyCode,
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.
*/
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)
/**
* 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())
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))
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
* 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
@ -191,12 +190,12 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
}
fun Cash.State.ownedBy(owner: PublicKey) = 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.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))))
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)))
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`(deposit: PartyAndReference) = issuedBy(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" */
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 */
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey)
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, 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 [NULL_PARTY] */
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.crypto.SecureHash
import net.corda.core.crypto.newSecureRandom
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.util.*
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -94,21 +94,21 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
override val amount: Amount<Issued<Commodity>>,
/** There must be a MoveCommand signed by this key to claim the amount */
override val owner: PublicKey
override val owner: AbstractParty
) : 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)
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 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)
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
@ -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.
*/
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)
/**
* 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())
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))
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.core.contracts.*
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.AnonymousParty
import net.corda.core.identity.Party
@ -22,6 +24,36 @@ import java.security.PublicKey
import java.time.Duration
import java.time.Instant
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.
val OBLIGATION_PROGRAM_ID = Obligation<Currency>()
@ -274,27 +306,20 @@ class Obligation<P : Any> : Contract {
data class State<P : Any>(
var lifecycle: Lifecycle = Lifecycle.NORMAL,
/** Where the debt originates from (obligor) */
val obligor: AnonymousParty,
val obligor: AbstractParty,
val template: Terms<P>,
val quantity: Long,
/** 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>> {
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 contract = OBLIGATION_PROGRAM_ID
override val exitKeys: Collection<PublicKey> = setOf(beneficiary)
override val exitKeys: Collection<PublicKey> = setOf(beneficiary.owningKey)
val dueBefore: Instant = template.dueBefore
override val participants: List<PublicKey> = listOf(obligor.owningKey, beneficiary)
override val owner: PublicKey = beneficiary
override val participants: List<AbstractParty> = listOf(obligor, 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)
override fun toString() = when (lifecycle) {
@ -305,7 +330,7 @@ class Obligation<P : Any> : Contract {
override val bilateralNetState: BilateralNetState<P>
get() {
check(lifecycle == Lifecycle.NORMAL)
return BilateralNetState(setOf(obligor.owningKey, beneficiary), template)
return BilateralNetState(setOf(obligor, beneficiary), template)
}
override val multilateralNetState: MultilateralNetState<P>
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
@ -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()
requireThat {
"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).
*/
fun generateCloseOutNetting(tx: TransactionBuilder,
signer: PublicKey,
signer: AbstractParty,
vararg states: State<P>) {
val netState = states.firstOrNull()?.bilateralNetState
@ -447,7 +472,7 @@ class Obligation<P : Any> : Contract {
val out = states.reduce(State<P>::net)
if (out.quantity > 0L)
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,
issuanceDef: Terms<P>,
pennies: Long,
beneficiary: PublicKey,
beneficiary: AbstractParty,
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,
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 })
}
val groups = states.groupBy { it.multilateralNetState }
val partyLookup = HashMap<PublicKey, AnonymousParty>()
val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).toSet()
val partyLookup = HashMap<PublicKey, AbstractParty>()
val signers = states.map { it.beneficiary }.union(states.map { it.obligor }).toSet()
// Create a lookup table of the party that each public key represents.
states.map { it.obligor }.forEach { partyLookup.put(it.owningKey, it) }
@ -502,12 +527,12 @@ class Obligation<P : Any> : Contract {
netBalances
// Convert the balances into obligation state objects
.map { entry ->
State(Lifecycle.NORMAL, partyLookup[entry.key.first]!!,
State(Lifecycle.NORMAL, entry.key.first,
netState.template, entry.value.quantity, entry.key.second)
}
// Add the new states to the TX
.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
val groups = statesAndRefs.groupBy { it.state.data.amount.token }
for ((_, stateAndRefs) in groups) {
val partiesUsed = ArrayList<PublicKey>()
val partiesUsed = ArrayList<AbstractParty>()
stateAndRefs.forEach { stateAndRef ->
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
tx.addInputState(stateAndRef)
tx.addOutputState(outState, notary)
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)
}
@ -578,7 +603,7 @@ class Obligation<P : Any> : Contract {
val template: Terms<P> = issuanceDef.product
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
var obligationRemaining: Amount<P> = obligationTotal
val assetSigners = HashSet<PublicKey>()
val assetSigners = HashSet<AbstractParty>()
statesAndRefs.forEach { tx.addInputState(it) }
@ -611,7 +636,7 @@ class Obligation<P : Any> : Contract {
}
// 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)
}
@ -630,11 +655,11 @@ class Obligation<P : Any> : Contract {
*
* @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>>> {
val balances = HashMap<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<AbstractParty, AbstractParty>, Amount<Obligation.Terms<P>>>()
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)
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.
*/
fun <P : Any> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<Pair<PublicKey, PublicKey>, Amount<P>> {
val nettedBalances = HashMap<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<P, P>, Amount<T>>()
balances.forEach { balance ->
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
* 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> {
val sum = HashMap<PublicKey, Long>()
fun <P: AbstractParty, T : Any> sumAmountsDue(balances: Map<Pair<P, P>, Amount<T>>): Map<P, Long> {
val sum = HashMap<P, Long>()
// Fill the map with zeroes initially
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)
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>.`owned by`(owner: PublicKey) = 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>.between(parties: Pair<AbstractParty, AbstractParty>) = copy(obligor = parties.first, beneficiary = parties.second)
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)
// 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)
@ -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>
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
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
import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.core.contracts.*
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace
@ -51,9 +51,9 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
@JvmStatic
fun <S : FungibleAsset<T>, T: Any> generateSpend(tx: TransactionBuilder,
amount: Amount<T>,
to: PublicKey,
to: AbstractParty,
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>> {
// Discussion
//
@ -90,7 +90,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
} else {
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 coins = it.value
@ -165,7 +165,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
@JvmStatic
fun <S : FungibleAsset<T>, T: Any> generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
generateMoveCommand: () -> CommandData,
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey {
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 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 })
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
* 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.core.contracts.*
import net.corda.core.contracts.clauses.Clause
import net.corda.core.identity.AbstractParty
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.loggerFor
import java.security.PublicKey
@ -31,7 +32,7 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
@Throws(InsufficientBalanceException::class)
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
generateMoveCommand: () -> CommandData,
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey
= 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.core.contracts.*
import net.corda.core.contracts.clauses.Clause
import net.corda.core.identity.AbstractParty
import java.security.PublicKey
/**
@ -22,7 +23,7 @@ interface NetState<P : Any> {
* Bilateral states are used in close-out netting.
*/
data class BilateralNetState<P : Any>(
val partyKeys: Set<PublicKey>,
val partyKeys: Set<AbstractParty>,
override val template: Obligation.Terms<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
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) {
// For close-out netting, allow any involved party to sign
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.UniqueIdentifier
import 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.transactions.TransactionBuilder
@ -17,12 +18,12 @@ class DummyDealContract : Contract {
data class State(
override val contract: Contract = DummyDealContract(),
override val participants: List<PublicKey> = listOf(),
override val participants: List<AbstractParty> = listOf(),
override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val ref: String,
override val parties: List<AnonymousParty> = listOf()) : DealState {
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 {

View File

@ -4,9 +4,9 @@ import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause
import net.corda.core.contracts.clauses.FilterOn
import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny
import net.corda.core.identity.AbstractParty
import java.security.PublicKey
class DummyLinearContract : Contract {
@ -20,11 +20,11 @@ class DummyLinearContract : Contract {
data class State(
override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val contract: Contract = DummyLinearContract(),
override val participants: List<PublicKey> = listOf(),
override val participants: List<AbstractParty> = listOf(),
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
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_KEY
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.node.ServiceHub
import net.corda.core.node.services.Vault
@ -21,13 +22,15 @@ import java.util.*
@JvmOverloads
fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
revisions: Int? = 0,
participants: List<PublicKey> = emptyList()) : Vault<DealState> {
participants: List<AbstractParty> = emptyList()) : Vault<DealState> {
val freshKey = keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val recipient = AnonymousParty(freshPublicKey)
val transactions: List<SignedTransaction> = dealIds.map {
// Issue a deal state
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(DUMMY_NOTARY_KEY)
}
@ -47,13 +50,15 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
@JvmOverloads
fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
uid: UniqueIdentifier = UniqueIdentifier(),
participants: List<PublicKey> = emptyList()) : Vault<LinearState> {
participants: List<AbstractParty> = emptyList()) : Vault<LinearState> {
val freshKey = keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val recipient = AnonymousParty(freshPublicKey)
val transactions: List<SignedTransaction> = (1..numberToCreate).map {
// Issue a Linear state
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(DUMMY_NOTARY_KEY)
}
@ -87,18 +92,19 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
atMostThisManyStates: Int = 10,
rng: Random = Random(),
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
ownedBy: PublicKey? = null,
ownedBy: AbstractParty? = null,
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault<Cash.State> {
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.
val cash = Cash()
val transactions: List<SignedTransaction> = amounts.map { pennies ->
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)
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?
val participants: Set<Party> = inputStates
.filterIsInstance<Cash.State>()
.map { serviceHub.identityService.partyFromKey(it.owner) }
.map { serviceHub.identityService.partyFromAnonymous(it.owner) }
.filterNotNull()
.toSet()

View File

@ -37,7 +37,8 @@ class CashIssueFlow(val amount: Amount<Currency>,
progressTracker.currentStep = GENERATING_TX
val builder: TransactionBuilder = TransactionType.General.Builder(notary = null)
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
val myKey = serviceHub.legalIdentityKey
builder.signWith(myKey)

View File

@ -39,7 +39,8 @@ open class CashPaymentFlow(
serviceHub.vaultService.generateSpend(
builder,
amount,
recipient.owningKey,
// TODO: Get a transaction key, don't just re-use the owning key
recipient,
issuerConstraint)
} catch (e: InsufficientBalanceException) {
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.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.node.NodeInfo
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
* 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.
*
* 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.
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")
it
@ -219,7 +220,7 @@ object TwoPartyTradeFlow {
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
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.
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
// 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.
val freshKey = serviceHub.keyManagementService.freshKey()
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public)
val freshPublicKey = serviceHub.keyManagementService.freshKey().public
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(AnonymousParty(freshPublicKey))
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
// to have one.

View File

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

View File

@ -4,15 +4,13 @@ import net.corda.contracts.asset.*
import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.core.contracts.*
import net.corda.core.days
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY
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.core.utilities.*
import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction
import net.corda.testing.*
@ -38,7 +36,7 @@ interface ICommercialPaperTestTemplate {
class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State(
MEGA_CORP.ref(123),
MEGA_CORP_PUBKEY,
MEGA_CORP,
1000.DOLLARS `issued by` MEGA_CORP.ref(123),
TEST_TX_TIME + 7.days
)
@ -51,7 +49,7 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = CommercialPaper.State(
issuance = MEGA_CORP.ref(123),
owner = MEGA_CORP_PUBKEY,
owner = MEGA_CORP,
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
maturityDate = TEST_TX_TIME + 7.days
)
@ -64,7 +62,7 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate {
override fun getPaper(): ICommercialPaperState = CommercialPaperLegacy.State(
issuance = MEGA_CORP.ref(123),
owner = MEGA_CORP_PUBKEY,
owner = MEGA_CORP,
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
maturityDate = TEST_TX_TIME + 7.days
)
@ -91,8 +89,8 @@ class CommercialPaperTestsGeneric {
val someProfits = 1200.DOLLARS `issued by` issuer
ledger {
unverifiedTransaction {
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
output("some profits", someProfits.STATE `owned by` MEGA_CORP_PUBKEY)
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE)
output("some profits", someProfits.STATE `owned by` MEGA_CORP)
}
// Some CP is issued onto the ledger by MegaCorp.
@ -108,8 +106,8 @@ class CommercialPaperTestsGeneric {
transaction("Trade") {
input("paper")
input("alice's $900")
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE_PUBKEY }
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP }
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE }
command(ALICE_PUBKEY) { Cash.Commands.Move() }
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
this.verifies()
@ -122,8 +120,8 @@ class CommercialPaperTestsGeneric {
input("some profits")
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY }
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY }
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE }
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP }
}
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
@ -270,8 +268,8 @@ class CommercialPaperTestsGeneric {
// Alice pays $9000 to BigCorp to own some of their debt.
moveTX = run {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public)
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public)
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, AnonymousParty(bigCorpServices.key.public))
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), AnonymousParty(aliceServices.key.public))
ptx.signWith(bigCorpServices.key)
ptx.signWith(aliceServices.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.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.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.node.services.VaultService
import net.corda.core.node.services.unconsumedStates
@ -23,7 +25,6 @@ import org.junit.Before
import org.junit.Test
import java.io.Closeable
import java.security.KeyPair
import java.security.PublicKey
import java.util.*
import kotlin.test.*
@ -32,11 +33,11 @@ class CashTests {
val defaultIssuer = MEGA_CORP.ref(defaultRef)
val inState = Cash.State(
amount = 1000.DOLLARS `issued by` defaultIssuer,
owner = DUMMY_PUBKEY_1
owner = AnonymousParty(DUMMY_PUBKEY_1)
)
// Input state held by the issuer
val issuerInState = inState.copy(owner = defaultIssuer.party.owningKey)
val outState = issuerInState.copy(owner = DUMMY_PUBKEY_2)
val issuerInState = inState.copy(owner = defaultIssuer.party)
val outState = issuerInState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2))
fun Cash.State.editDepositRef(ref: Byte) = copy(
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,
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,
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,
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,
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()
}
@ -142,7 +143,7 @@ class CashTests {
output {
Cash.State(
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
owner = DUMMY_PUBKEY_1
owner = AnonymousParty(DUMMY_PUBKEY_1)
)
}
tweak {
@ -158,14 +159,14 @@ class CashTests {
fun generateIssueRaw() {
// Test generation works.
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)
}.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty())
val s = tx.outputs[0].data as Cash.State
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
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)
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
}
@ -175,7 +176,7 @@ class CashTests {
// Test issuance from an issued amount
val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34)
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)
}.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty())
@ -248,14 +249,14 @@ class CashTests {
// Issue some cash
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)
val tx = ptx.toSignedTransaction()
// Include the previously issued cash in a new issuance command
ptx = TransactionType.General.Builder(DUMMY_NOTARY)
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
@ -329,7 +330,7 @@ class CashTests {
input {
inState.copy(
amount = 150.POUNDS `issued by` defaultIssuer,
owner = DUMMY_PUBKEY_2
owner = AnonymousParty(DUMMY_PUBKEY_2)
)
}
output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
@ -382,10 +383,10 @@ class CashTests {
// Multi-issuer case.
transaction {
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(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() }
@ -420,19 +421,19 @@ class CashTests {
// Can't merge them together.
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"
}
// Missing MiniCorp deposit
tweak {
output { inState.copy(owner = DUMMY_PUBKEY_2) }
output { inState.copy(owner = DUMMY_PUBKEY_2) }
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
this `fails with` "the amounts balance"
}
// This works.
output { inState.copy(owner = DUMMY_PUBKEY_2) }
output { inState.copy(owner = DUMMY_PUBKEY_2) `issued by` MINI_CORP }
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) `issued by` MINI_CORP }
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
this.verifies()
}
@ -442,11 +443,11 @@ class CashTests {
fun multiCurrency() {
// Check we can do an atomic currency trade tx.
transaction {
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), DUMMY_PUBKEY_2)
input { inState `owned by` DUMMY_PUBKEY_1 }
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(DUMMY_PUBKEY_2))
input { inState `owned by` AnonymousParty(DUMMY_PUBKEY_1) }
input { pounds }
output { inState `owned by` DUMMY_PUBKEY_2 }
output { pounds `owned by` DUMMY_PUBKEY_1 }
output { inState `owned by` AnonymousParty(DUMMY_PUBKEY_2) }
output { pounds `owned by` AnonymousParty(DUMMY_PUBKEY_1) }
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Cash.Commands.Move() }
this.verifies()
@ -458,13 +459,13 @@ class CashTests {
// Spend tx generation
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) =
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))
)
@ -484,7 +485,7 @@ class CashTests {
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)
database.transaction {
vault.generateSpend(tx, amount, dest)
@ -566,13 +567,13 @@ class CashTests {
database.transaction {
val wtx = makeSpend(100.DOLLARS, THEIR_PUBKEY_1)
val wtx = makeSpend(100.DOLLARS, THEIR_IDENTITY_1)
@Suppress("UNCHECKED_CAST")
val vaultState = vaultStatesUnconsumed.elementAt(0)
assertEquals(vaultState.ref, wtx.inputs[0])
assertEquals(vaultState.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[0].data)
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1), wtx.outputs[0].data)
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
}
}
@ -582,7 +583,7 @@ class CashTests {
database.transaction {
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])
}
@ -593,14 +594,14 @@ class CashTests {
database.transaction {
val wtx = makeSpend(10.DOLLARS, THEIR_PUBKEY_1)
val wtx = makeSpend(10.DOLLARS, THEIR_IDENTITY_1)
@Suppress("UNCHECKED_CAST")
val vaultState = vaultStatesUnconsumed.elementAt(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(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() {
database.transaction {
val wtx = makeSpend(500.DOLLARS, THEIR_PUBKEY_1)
val wtx = makeSpend(500.DOLLARS, THEIR_IDENTITY_1)
@Suppress("UNCHECKED_CAST")
val vaultState0 = vaultStatesUnconsumed.elementAt(0)
val vaultState1 = vaultStatesUnconsumed.elementAt(1)
assertEquals(vaultState0.ref, wtx.inputs[0])
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(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
}
}
@ -624,7 +625,7 @@ class CashTests {
fun generateSpendMixedDeposits() {
database.transaction {
val wtx = makeSpend(580.DOLLARS, THEIR_PUBKEY_1)
val wtx = makeSpend(580.DOLLARS, THEIR_IDENTITY_1)
assertEquals(3, wtx.inputs.size)
@Suppress("UNCHECKED_CAST")
@ -635,9 +636,9 @@ class CashTests {
assertEquals(vaultState0.ref, wtx.inputs[0])
assertEquals(vaultState1.ref, wtx.inputs[1])
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(vaultState2.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[0].data)
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
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_IDENTITY_1), wtx.outputs[0].data)
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
}
}
@ -647,12 +648,12 @@ class CashTests {
database.transaction {
val e: InsufficientBalanceException = assertFailsWith("balance") {
makeSpend(1000.DOLLARS, THEIR_PUBKEY_1)
makeSpend(1000.DOLLARS, THEIR_IDENTITY_1)
}
assertEquals((1000 - 580).DOLLARS, e.amountMissing)
assertFailsWith(InsufficientBalanceException::class) {
makeSpend(81.SWISS_FRANCS, THEIR_PUBKEY_1)
makeSpend(81.SWISS_FRANCS, THEIR_IDENTITY_1)
}
}
}
@ -662,9 +663,9 @@ class CashTests {
*/
@Test
fun aggregation() {
val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` MEGA_CORP.ref(2), MEGA_CORP_PUBKEY)
val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` MEGA_CORP.ref(2), MINI_CORP_PUBKEY)
val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), 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)
val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), MEGA_CORP)
// Obviously it must be possible to aggregate states with themselves
assertEquals(fiveThousandDollarsFromMega.amount.token, fiveThousandDollarsFromMega.amount.token)
@ -678,7 +679,7 @@ class CashTests {
// States cannot be aggregated if the currency differs
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
assertNotEquals(fiveThousandDollarsFromMega.amount.token, (fiveThousandDollarsFromMega `with deposit` defaultIssuer).amount.token)
@ -688,20 +689,20 @@ class CashTests {
@Test
fun `summing by owner`() {
val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP_PUBKEY),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY)
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
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)
fun `summing by owner throws`() {
val states = listOf(
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
Cash.State(4000.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)
)
states.sumCashBy(MINI_CORP_PUBKEY)
states.sumCashBy(MINI_CORP)
}
@Test
@ -720,9 +721,9 @@ class CashTests {
@Test
fun `summing a single currency`() {
val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
Cash.State(4000.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),
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
)
// Test that summing everything produces the total number of dollars
val expected = 7000.DOLLARS `issued by` defaultIssuer
@ -733,8 +734,8 @@ class CashTests {
@Test(expected = IllegalArgumentException::class)
fun `summing multiple currencies`() {
val states = listOf(
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP_PUBKEY)
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP)
)
// Test that summing everything fails because we're mixing units
states.sumCash()
@ -748,14 +749,14 @@ class CashTests {
output("MEGA_CORP cash") {
Cash.State(
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
owner = MEGA_CORP_PUBKEY
owner = MEGA_CORP
)
}
}
transaction {
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() }
this.verifies()
}
@ -764,7 +765,7 @@ class CashTests {
transaction {
input("MEGA_CORP cash")
// 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() }
this.verifies()
}

View File

@ -2,13 +2,14 @@ package net.corda.contracts.asset
import net.corda.contracts.asset.Obligation.Lifecycle
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.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.utilities.*
import net.corda.testing.*
import org.junit.Test
import java.security.PublicKey
import java.time.Duration
import java.time.temporal.ChronoUnit
import java.util.*
@ -33,18 +34,18 @@ class ObligationTests {
obligor = MEGA_CORP,
template = megaCorpDollarSettlement,
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(
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
) = group.apply {
unverifiedTransaction {
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY))
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY))
output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB_PUBKEY))
output("Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_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))
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)
}
}
@ -71,13 +72,13 @@ class ObligationTests {
tweak {
output { outState }
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"
}
// Simple reallocation works.
tweak {
output { outState }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this.verifies()
}
}
@ -98,7 +99,7 @@ class ObligationTests {
// institution is allowed to issue as much cash as they want.
transaction {
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"
}
transaction {
@ -106,7 +107,7 @@ class ObligationTests {
Obligation.State(
obligor = MINI_CORP,
quantity = 1000.DOLLARS.quantity,
beneficiary = DUMMY_PUBKEY_1,
beneficiary = CHARLIE,
template = megaCorpDollarSettlement
)
}
@ -121,14 +122,14 @@ class ObligationTests {
// Test generation works.
val tx = TransactionType.General.Builder(notary = null).apply {
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)
}.toSignedTransaction().tx
assertTrue(tx.inputs.isEmpty())
val expected = Obligation.State(
obligor = MINI_CORP,
quantity = 100.DOLLARS.quantity,
beneficiary = DUMMY_PUBKEY_1,
beneficiary = CHARLIE,
template = megaCorpDollarSettlement
)
assertEquals(tx.outputs[0].data, expected)
@ -142,7 +143,7 @@ class ObligationTests {
// Move fails: not allowed to summon money.
tweak {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance"
}
@ -199,7 +200,7 @@ class ObligationTests {
// Issue some obligation
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
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)
}.toSignedTransaction()
@ -207,16 +208,16 @@ class ObligationTests {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
ptx.addInputState(tx.tx.outRef<Obligation.State<Currency>>(0))
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
fun `generate close-out net transaction`() {
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY)
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
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(DUMMY_NOTARY_KEY)
}.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
fun `generate close-out net transaction with remainder`() {
val obligationAliceToBob = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB_PUBKEY)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY)
val obligationAliceToBob = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
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(DUMMY_NOTARY_KEY)
}.toSignedTransaction().tx
assertEquals(1, tx.outputs.size)
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
fun `generate payment net transaction`() {
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY)
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
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
fun `generate payment net transaction with remainder`() {
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE_PUBKEY)
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)
val tx = TransactionType.General.Builder(null).apply {
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
signWith(ALICE_KEY)
@ -278,7 +279,7 @@ class ObligationTests {
// Generate a transaction issuing the obligation
var tx = TransactionType.General.Builder(null).apply {
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)
}.toSignedTransaction()
var stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0)
@ -309,14 +310,14 @@ class ObligationTests {
@Test
fun `generate settlement transaction`() {
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)
}.toSignedTransaction().tx
// Generate a transaction issuing the obligation
val obligationTx = TransactionType.General.Builder(null).apply {
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)
}.toSignedTransaction().tx
@ -354,7 +355,7 @@ class ObligationTests {
input("Alice's $1,000,000 obligation to Bob")
input("Bob's $1,000,000 obligation to Alice")
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) }
timestamp(TEST_TX_TIME)
this.verifies()
@ -368,7 +369,7 @@ class ObligationTests {
transaction("Issuance") {
input("Alice's $1,000,000 obligation to Bob")
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) }
timestamp(TEST_TX_TIME)
this `fails with` "amounts owed on input and output must match"
@ -422,7 +423,7 @@ class ObligationTests {
transaction("Issuance") {
input("Bob's $1,000,000 obligation to Alice")
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) }
timestamp(TEST_TX_TIME)
this.verifies()
@ -436,7 +437,7 @@ class ObligationTests {
transaction("Issuance") {
input("Bob's $1,000,000 obligation to Alice")
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) }
timestamp(TEST_TX_TIME)
this `fails with` "all involved parties have signed"
@ -452,7 +453,7 @@ class ObligationTests {
transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob")
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) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this.verifies()
@ -463,10 +464,10 @@ class ObligationTests {
val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer
ledger {
transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY))
input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY)
output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) }
output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY }
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
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 }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this.verifies()
@ -474,12 +475,12 @@ class ObligationTests {
}
// 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 {
transaction("Settlement") {
input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob
input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY)
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_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 }
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this `fails with` "all inputs are in the normal state"
@ -492,7 +493,7 @@ class ObligationTests {
transaction("Settlement") {
input("Alice's $1,000,000 obligation to Bob")
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) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
this `fails with` "amount in settle command"
@ -506,17 +507,17 @@ class ObligationTests {
val oneUnitFcoj = Amount(1, defaultFcoj)
val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME)
val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE,
obligationDef, oneUnitFcoj.quantity, NullPublicKey)
obligationDef, oneUnitFcoj.quantity, NULL_PARTY)
// Try settling a simple commodity obligation
ledger {
unverifiedTransaction {
output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB_PUBKEY))
output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE_PUBKEY))
output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB))
output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE))
}
transaction("Settlement") {
input("Alice's 1 FCOJ obligation to Bob")
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) { CommodityContract.Commands.Move(Obligation<Commodity>().legalContractReference) }
verifies()
@ -531,7 +532,7 @@ class ObligationTests {
cashObligationTestRoots(this)
transaction("Settlement") {
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) }
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 futureTestTime = TEST_TX_TIME + Duration.ofDays(7)
transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `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) }
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) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
timestamp(TEST_TX_TIME)
this `fails with` "the due date has passed"
@ -551,8 +552,8 @@ class ObligationTests {
// Try defaulting an obligation that is now in the past
ledger {
transaction("Settlement") {
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `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) }
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) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
timestamp(TEST_TX_TIME)
this.verifies()
@ -565,7 +566,7 @@ class ObligationTests {
fun testMergeSplit() {
// Splitting value works.
transaction {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
tweak {
input { inState }
repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } }
@ -624,7 +625,7 @@ class ObligationTests {
inState.copy(
quantity = 15000,
template = megaCorpPoundSettlement,
beneficiary = DUMMY_PUBKEY_2
beneficiary = AnonymousParty(DUMMY_PUBKEY_2)
)
}
output { outState.copy(quantity = 115000) }
@ -635,7 +636,7 @@ class ObligationTests {
input { inState }
input { inState `issued by` MINI_CORP }
output { outState }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance"
}
}
@ -648,17 +649,17 @@ class ObligationTests {
output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
tweak {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this `fails with` "the amounts balance"
}
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"
tweak {
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
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 = 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"
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"
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()
}
}
@ -697,20 +698,20 @@ class ObligationTests {
// Can't merge them together.
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"
}
// Missing MiniCorp deposit
tweak {
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
this `fails with` "the amounts balance"
}
// This works.
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) `issued by` MINI_CORP }
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) `issued by` MINI_CORP }
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
this.verifies()
}
}
@ -719,12 +720,12 @@ class ObligationTests {
fun multiCurrency() {
// Check we can do an atomic currency trade tx.
transaction {
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, DUMMY_PUBKEY_2)
input { inState `owned by` DUMMY_PUBKEY_1 }
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(DUMMY_PUBKEY_2))
input { inState `owned by` CHARLIE }
input { pounds }
output { inState `owned by` DUMMY_PUBKEY_2 }
output { pounds `owned by` DUMMY_PUBKEY_1 }
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Obligation.Commands.Move() }
output { inState `owned by` AnonymousParty(DUMMY_PUBKEY_2) }
output { pounds `owned by` CHARLIE }
command(CHARLIE.owningKey, DUMMY_PUBKEY_2) { Obligation.Commands.Move() }
this.verifies()
}
@ -733,11 +734,11 @@ class ObligationTests {
@Test
fun `nettability of settlement contracts`() {
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,
2000.DOLLARS.quantity, MINI_CORP_PUBKEY)
2000.DOLLARS.quantity, MINI_CORP)
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
assertEquals(fiveKDollarsFromMegaToMega.bilateralNetState, fiveKDollarsFromMegaToMega.bilateralNetState)
@ -778,9 +779,9 @@ class ObligationTests {
@Test
fun `extraction of issuance defintion`() {
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,
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY)
1000.DOLLARS.quantity, MEGA_CORP)
// Issuance definitions must match the input
assertEquals(fiveKDollarsFromMegaToMega.template, megaCorpDollarSettlement)
@ -791,14 +792,14 @@ class ObligationTests {
fun `adding two settlement contracts nets them`() {
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
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,
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY)
1000.DOLLARS.quantity, MEGA_CORP)
var actual = fiveKDollarsFromMegaToMini.net(fiveKDollarsFromMegaToMini.copy(quantity = 2000.DOLLARS.quantity))
// Both pay from mega to mini, so we add directly
var expected = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, 7000.DOLLARS.quantity,
MINI_CORP_PUBKEY)
MINI_CORP)
assertEquals(expected, actual)
// 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
assertFailsWith(IllegalArgumentException::class) {
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`() {
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
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 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))
assertEquals(expected, actual)
}
@ -827,24 +828,24 @@ class ObligationTests {
@Test
fun `netting equal balances due between parties`() {
// Now try it with two balances, which cancel each other out
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE, BOB), 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 actual: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = netAmountsDue(balanced)
val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
val actual: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = netAmountsDue(balanced)
assertEquals(expected, actual)
}
@Test
fun `netting difference balances due between parties`() {
// Now try it with two balances, which cancel each other out
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(200000000, GBP))
val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE, BOB), Amount(100000000, GBP)),
Pair(Pair(BOB, ALICE), Amount(200000000, GBP))
)
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(BOB, ALICE), Amount(100000000, GBP))
)
val actual = netAmountsDue(balanced)
assertEquals(expected, actual)
@ -852,16 +853,16 @@ class ObligationTests {
@Test
fun `summing empty balances due between parties`() {
val empty = emptyMap<Pair<PublicKey, PublicKey>, Amount<Currency>>()
val expected = emptyMap<PublicKey, Long>()
val empty = emptyMap<Pair<AbstractParty, AbstractParty>, Amount<Currency>>()
val expected = emptyMap<AbstractParty, Long>()
val actual = sumAmountsDue(empty)
assertEquals(expected, actual)
}
@Test
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 expected: Map<PublicKey, Long> = mapOf(Pair(ALICE_PUBKEY, -100000000L), Pair(BOB_PUBKEY, 100000000L))
val simple: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(Pair(Pair(ALICE, BOB), Amount(100000000, GBP)))
val expected: Map<AbstractParty, Long> = mapOf(Pair(ALICE, -100000000L), Pair(BOB, 100000000L))
val actual = sumAmountsDue(simple)
assertEquals(expected, actual)
}
@ -869,11 +870,11 @@ class ObligationTests {
@Test
fun `summing balances due between parties which net to zero`() {
// Now try it with two balances, which cancel each other out
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE, BOB), 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)
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.TransactionType
import net.corda.core.crypto.NullSignature
import net.corda.core.identity.AnonymousParty
import net.corda.core.testing.*
import net.corda.core.transactions.SignedTransaction
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 {
return Cash.State(
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()
val paymentTx = future.getOrThrow()
val states = paymentTx.tx.outputs.map { it.data }.filterIsInstance<Cash.State>()
val ourState = states.single { it.owner != payTo.owningKey }
val paymentState = states.single { it.owner == payTo.owningKey }
val ourState = states.single { it.owner.owningKey != payTo.owningKey }
val paymentState = states.single { it.owner.owningKey == payTo.owningKey }
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.platform.Generic
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.node.services.Vault
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.serialize
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_KEY
import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.core.utilities.DUMMY_PUBKEY_2
import org.h2.jdbcx.JdbcDataSource
import org.junit.After
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import rx.Observable
import java.security.PublicKey
import java.time.Instant
import java.util.*
import java.util.concurrent.CountDownLatch
@ -81,12 +84,12 @@ class VaultSchemaTest {
private class VaultNoopContract : Contract {
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 participants: List<PublicKey>
override val participants: List<AbstractParty>
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 {
@ -101,10 +104,10 @@ class VaultSchemaTest {
private fun setupDummyData() {
// dummy Transaction
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,
listOf(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2)), notary)
val inState3 = TransactionState(VaultNoopContract.VaultNoopState(DUMMY_PUBKEY_1), notary)
listOf(ALICE, BOB)), notary)
val inState3 = TransactionState(VaultNoopContract.VaultNoopState(ALICE), notary)
val outState1 = inState1.copy()
val outState2 = inState2.copy()
val outState3 = inState3.copy()
@ -132,9 +135,9 @@ class VaultSchemaTest {
private fun createTxnWithTwoStateTypes(): LedgerTransaction {
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,
listOf(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2)), notary)
listOf(ALICE, BOB)), notary)
val outState1 = inState1.copy()
val outState2 = inState2.copy()
val state1TxHash = SecureHash.randomSHA256()

View File

@ -57,7 +57,7 @@ class BFTNotaryServiceTests : NodeBasedTest() {
firstSpend.resultFuture.getOrThrow()
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)
signWith(aliceKey)
toSignedTransaction(false)

View File

@ -46,7 +46,7 @@ class RaftNotaryServiceTests : NodeBasedTest() {
firstSpend.resultFuture.getOrThrow()
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)
signWith(aliceKey)
toSignedTransaction(false)

View File

@ -115,7 +115,7 @@ class ContractUpgradeHandler(otherSide: Party) : AbstractStateReplacementFlow.Ac
val proposedTx = proposal.stx.tx
val expectedTx = ContractUpgradeFlow.assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction()
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 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.requireThat
import net.corda.core.crypto.toStringShort
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.node.services.IdentityService
@ -16,7 +17,6 @@ import java.security.cert.*
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import javax.annotation.concurrent.ThreadSafe
import javax.security.auth.x500.X500Principal
/**
* Simple identity service which caches parties and provides functionality for efficient lookup.
@ -44,7 +44,7 @@ class InMemoryIdentityService : SingletonSerializeAsToken(), IdentityService {
@Deprecated("Use partyFromX500Name")
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
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)
@Throws(IdentityService.UnknownAnonymousPartyException::class)

View File

@ -11,11 +11,12 @@ import io.requery.kotlin.notNull
import io.requery.query.RowExpression
import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.OnLedgerAsset
import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.core.ThreadBox
import net.corda.core.bufferUntilSubscribed
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.Party
import net.corda.core.node.ServiceHub
@ -468,7 +469,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
@Suspendable
override fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>,
to: PublicKey,
to: AbstractParty,
onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<PublicKey>> {
// Retrieve unspent and unlocked cash states that meet our spending criteria.
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() })
}
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))
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) {
is OwnableState -> state.owner.containsAny(ourKeys)
is OwnableState -> state.owner.owningKey.containsAny(ourKeys)
// It's potentially of interest to the vault
is LinearState -> state.isRelevant(ourKeys)
else -> false

View File

@ -91,7 +91,7 @@ class CordaRPCOpsImplTest {
val expectedState = Cash.State(Amount(quantity,
Issued(aliceNode.info.legalIdentity.ref(ref), GBP)),
recipient.owningKey)
recipient)
var issueSmId: StateMachineRunId? = null
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.StateMachineRunId
import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.map
import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo
@ -97,7 +98,7 @@ class TwoPartyTradeFlowTests {
}
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
}
@ -144,7 +145,7 @@ class TwoPartyTradeFlowTests {
}
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
}
@ -195,7 +196,7 @@ class TwoPartyTradeFlowTests {
bobNode.services.fillWithSomeTestCash(2000.DOLLARS, outputNotary = notaryNode.info.notaryIdentity)
}
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
}
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey)
@ -301,6 +302,7 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name)
val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name)
val alice = aliceNode.info.legalIdentity
val aliceKey = aliceNode.services.legalIdentityKey
ledger(aliceNode.services) {
@ -317,12 +319,13 @@ class TwoPartyTradeFlowTests {
}
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,
notaryNode.info.notaryIdentity).second
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey)
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
}
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey)
@ -416,13 +419,14 @@ class TwoPartyTradeFlowTests {
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,
notaryNode.info.notaryIdentity).second
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode)
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
}
@ -520,14 +524,16 @@ class TwoPartyTradeFlowTests {
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
val alice = aliceNode.info.legalIdentity
val aliceKey = aliceNode.services.legalIdentityKey
val bob = bobNode.info.legalIdentity
val bobKey = bobNode.services.legalIdentityKey
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
val alicesFakePaper = aliceNode.database.transaction {
fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey,
fillUpForSeller(aliceError, alice,
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
}
@ -571,16 +577,16 @@ class TwoPartyTradeFlowTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
withError: Boolean,
owner: PublicKey,
issuer: AnonymousParty,
owner: AbstractParty,
issuer: AbstractParty,
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
// wants to sell to Bob.
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
// Issued money to itself.
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwnerKey }
output("elbonian money 2", notary = notary) { 1000.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` interimOwner }
if (!withError) {
command(issuer.owningKey) { Cash.Commands.Issue() }
} else {
@ -599,15 +605,15 @@ class TwoPartyTradeFlowTests {
val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 1")
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()
}
val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
input("elbonian money 2")
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.
command(interimOwnerKey) { Cash.Commands.Move() }
output(notary = notary) { 700.DOLLARS.CASH `issued by` issuer `owned by` interimOwner } // Change output.
command(interimOwner.owningKey) { Cash.Commands.Move() }
this.verifies()
}
@ -617,7 +623,7 @@ class TwoPartyTradeFlowTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
withError: Boolean,
owner: PublicKey,
owner: AbstractParty,
amount: Amount<Issued<Currency>>,
attachmentID: SecureHash?,
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {

View File

@ -123,9 +123,9 @@ class NotaryChangeTests {
val owner = node.info.legalIdentity.ref(0)
val notary = notaryNode.info.notaryIdentity
val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey)
val stateB = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey)
val stateC = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey)
val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
val stateB = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
val stateC = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
val tx = TransactionType.General.Builder(null).apply {
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> {
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 nodeAKey = nodeA.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.SecureHash
import net.corda.core.crypto.toBase58String
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.Vault
import net.corda.core.serialization.serialize
import net.corda.core.serialization.storageKryo
@ -129,7 +130,7 @@ class RequeryConfigurationTest {
index = txnState.index
stateStatus = Vault.StateStatus.UNCONSUMED
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()
notaryKey = txn.tx.notary!!.owningKey.toBase58String()
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.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty
import net.corda.core.node.ServiceHub
import net.corda.core.node.services.VaultService
import net.corda.core.serialization.SingletonSerializeAsToken
@ -113,7 +114,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
}
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState {
override val participants: List<PublicKey>
override val participants: List<AbstractParty>
get() = throw UnsupportedOperationException()
override val linearId = UniqueIdentifier()

View File

@ -2,12 +2,13 @@ package net.corda.node.services.events
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.*
import net.corda.core.identity.Party
import net.corda.core.crypto.containsAny
import net.corda.core.flows.FlowInitiator
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory
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.linearHeadsOfType
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 {
return participants.any { it.containsAny(ourKeys) }
return participants.any { it.owningKey.containsAny(ourKeys) }
}
}

View File

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

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.node.services.Vault
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
@ -86,7 +87,7 @@ class HibernateObserverTests {
override val contract: Contract
get() = throw UnsupportedOperationException()
override val participants: List<CompositeKey>
override val participants: List<AbstractParty>
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.testing.fillWithSomeTestCash
import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.StatesNotAvailableException
import net.corda.core.node.services.TxWritableStorageService
import net.corda.core.node.services.VaultService
@ -401,11 +402,11 @@ class NodeVaultServiceTest {
fun addNoteToTransaction() {
database.transaction {
val freshKey = services.legalIdentityKey
val freshKey = services.legalIdentityKey.public
// Issue a txn to Send us some Money
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)
}.toSignedTransaction()
@ -418,7 +419,7 @@ class NodeVaultServiceTest {
// Issue more Money (GBP)
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)
}.toSignedTransaction()

View File

@ -206,9 +206,9 @@ class VaultQueryTests {
fun `unconsumed states by participants`() {
database.transaction {
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"), participants = listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY))
services.fillWithSomeTestDeals(listOf("456"), 3, participants = listOf(MEGA_CORP_PUBKEY, BIG_CORP_PUBKEY))
services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_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, BIG_CORP))
services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP, MINI_CORP))
// DOCSTART VaultQueryExample5
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.testing.*
import net.corda.core.contracts.*
import net.corda.core.identity.AnonymousParty
import net.corda.core.node.services.VaultService
import net.corda.core.node.services.consumedStates
import net.corda.core.node.services.unconsumedStates
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_KEY
import net.corda.core.utilities.LogHelper
import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.transaction
import net.corda.testing.BOB_PUBKEY
import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.node.MockServices
@ -79,7 +79,7 @@ class VaultWithCashTest {
val state = w[0].state.data
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.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[1].state.data).amount)
@ -91,8 +91,9 @@ class VaultWithCashTest {
database.transaction {
// A tx that sends us money.
val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
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)
}.toSignedTransaction()
@ -101,7 +102,7 @@ class VaultWithCashTest {
// A tx that spends our money.
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY)
vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -110,7 +111,7 @@ class VaultWithCashTest {
// A tx that doesn't send us anything.
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(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -128,13 +129,14 @@ class VaultWithCashTest {
@Test
fun `issue and attempt double spend`() {
val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
database.transaction {
// A tx that sends us money.
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L),
issuedBy = MEGA_CORP.ref(1),
issuerKey = MEGA_CORP_KEY,
ownedBy = freshKey.public)
ownedBy = AnonymousParty(freshPublicKey))
println("Cash balance: ${vault.cashBalances[USD]}")
assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10)
@ -149,7 +151,7 @@ class VaultWithCashTest {
try {
val txn1 =
TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 60.DOLLARS, BOB_PUBKEY)
vault.generateSpend(this, 60.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -181,7 +183,7 @@ class VaultWithCashTest {
try {
val txn2 =
TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY)
vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -219,12 +221,14 @@ class VaultWithCashTest {
fun `branching LinearStates fails to verify`() {
database.transaction {
val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val freshIdentity = AnonymousParty(freshPublicKey)
val linearId = UniqueIdentifier()
// Issue a linear state
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(freshKey.public)))
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -239,12 +243,14 @@ class VaultWithCashTest {
fun `sequencing LinearStates works`() {
database.transaction {
val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val freshIdentity = AnonymousParty(freshPublicKey)
val linearId = UniqueIdentifier()
// Issue a linear state
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(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -256,7 +262,7 @@ class VaultWithCashTest {
// Move the same state
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))
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -272,8 +278,9 @@ class VaultWithCashTest {
fun `spending cash in vault of mixed state types works`() {
val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
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.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
val cash = vault.unconsumedStates<Cash.State>()
@ -287,7 +294,7 @@ class VaultWithCashTest {
database.transaction {
// A tx that spends our money.
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY)
vault.generateSpend(this, 80.DOLLARS, BOB)
signWith(freshKey)
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()
@ -305,6 +312,8 @@ class VaultWithCashTest {
fun `consuming multiple contract state types in same transaction`() {
val freshKey = services.keyManagementService.freshKey()
val freshPublicKey = freshKey.public
val freshIdentity = AnonymousParty(freshPublicKey)
database.transaction {
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
@ -317,8 +326,8 @@ class VaultWithCashTest {
// Create a txn consuming different contract types
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(participants = listOf(freshKey.public)))
addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshKey.public)))
addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity)))
addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)))
addInputState(linearStates.first())
addInputState(deals.first())
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.StartableByRPC
import net.corda.core.getOrThrow
import net.corda.core.identity.AbstractParty
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startTrackedFlow
import net.corda.core.sizedInputStreamAndHash
@ -179,6 +180,6 @@ class AttachmentContract : Contract {
data class State(val hash: SecureHash.SHA256) : ContractState {
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 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()
}.cache().toBlocking()

View File

@ -40,22 +40,22 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
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()
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 {
val deals = states.map { it.state.data }
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()
vaultUpdates.notUsed()
val states = vault.filterStatesOfType<InterestRateSwap.State<*>>()
val states = vault.filterStatesOfType<InterestRateSwap.State>()
val swaps = states.map { it.state.data }.toTypedArray()
return swaps
}
@ -63,14 +63,14 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
@GET
@Path("deals")
@Produces(MediaType.APPLICATION_JSON)
fun fetchDeals(): Array<InterestRateSwap.State<*>> = getAllDeals()
fun fetchDeals(): Array<InterestRateSwap.State> = getAllDeals()
@POST
@Path("deals")
@Consumes(MediaType.APPLICATION_JSON)
fun storeDeal(newDeal: InterestRateSwap.State<Party>): Response {
fun storeDeal(newDeal: InterestRateSwap.State): Response {
return try {
rpc.startFlow(AutoOfferFlow::Requester, newDeal.toAnonymous()).returnValue.getOrThrow()
rpc.startFlow(AutoOfferFlow::Requester, newDeal).returnValue.getOrThrow()
Response.created(URI.create(generateDealLink(newDeal))).build()
} catch (ex: Throwable) {
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.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.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
@ -307,8 +308,8 @@ class InterestRateSwap : Contract {
}
@CordaSerializable
open class FixedLeg<P : AbstractParty>(
var fixedRatePayer: P,
open class FixedLeg(
var fixedRatePayer: AbstractParty,
notional: Amount<Currency>,
paymentFrequency: Frequency,
effectiveDate: LocalDate,
@ -335,7 +336,7 @@ class InterestRateSwap : Contract {
if (other?.javaClass != javaClass) return false
if (!super.equals(other)) return false
other as FixedLeg<*>
other as FixedLeg
if (fixedRatePayer != other.fixedRatePayer) 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)
// 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,
paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate,
@ -365,17 +366,11 @@ class InterestRateSwap : Contract {
fixedRatePayer, notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate,
terminationDateAdjustment, dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay,
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
open class FloatingLeg<P : AbstractParty>(
var floatingRatePayer: P,
open class FloatingLeg(
var floatingRatePayer: AbstractParty,
notional: Amount<Currency>,
paymentFrequency: Frequency,
effectiveDate: LocalDate,
@ -411,7 +406,7 @@ class InterestRateSwap : Contract {
if (other?.javaClass != javaClass) return false
if (!super.equals(other)) return false
other as FloatingLeg<*>
other as FloatingLeg
if (floatingRatePayer != other.floatingRatePayer) return false
if (rollConvention != other.rollConvention) return false
@ -433,7 +428,7 @@ class InterestRateSwap : Contract {
index, indexSource, indexTenor)
fun copy(floatingRatePayer: P = this.floatingRatePayer,
fun copy(floatingRatePayer: AbstractParty = this.floatingRatePayer,
notional: Amount<Currency> = this.notional,
paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate,
@ -462,13 +457,6 @@ class InterestRateSwap : Contract {
paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment, rollConvention,
fixingRollConvention, resetDayInMonth, fixingPeriod, resetRule, fixingsPerPayment,
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>())
@ -478,7 +466,7 @@ class InterestRateSwap : Contract {
* Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides
* 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
fun checkLegDates(legs: List<CommonLeg>) {
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 }
}
for (leg: CommonLeg in legs) {
if (leg is FixedLeg<*>) {
if (leg is FixedLeg) {
requireThat {
// TODO: Confirm: would someone really enter a swap with a negative fixed rate?
"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
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 }
}
}
@ -543,12 +531,12 @@ class InterestRateSwap : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java)
override fun verify(tx: TransactionForContract,
inputs: List<State<*>>,
outputs: List<State<*>>,
inputs: List<State>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Agree>()
val irs = outputs.filterIsInstance<State<*>>().single()
val irs = outputs.filterIsInstance<State>().single()
requireThat {
"There are no in states for an agreement" using inputs.isEmpty()
"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 fun verify(tx: TransactionForContract,
inputs: List<State<*>>,
outputs: List<State<*>>,
inputs: List<State>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Refix>()
val irs = outputs.filterIsInstance<State<*>>().single()
val prevIrs = inputs.filterIsInstance<State<*>>().single()
val irs = outputs.filterIsInstance<State>().single()
val prevIrs = inputs.filterIsInstance<State>().single()
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
@ -624,8 +612,8 @@ class InterestRateSwap : Contract {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java)
override fun verify(tx: TransactionForContract,
inputs: List<State<*>>,
outputs: List<State<*>>,
inputs: List<State>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> {
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 fun verify(tx: TransactionForContract,
inputs: List<State<*>>,
outputs: List<State<*>>,
inputs: List<State>,
outputs: List<State>,
commands: List<AuthenticatedObject<Commands>>,
groupingKey: UniqueIdentifier?): Set<Commands> {
val command = tx.commands.requireSingleCommand<Commands.Mature>()
val irs = inputs.filterIsInstance<State<*>>().single()
val irs = inputs.filterIsInstance<State>().single()
requireThat {
"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()
@ -667,9 +655,9 @@ class InterestRateSwap : Contract {
/**
* The state class contains the 4 major data classes.
*/
data class State<P : AbstractParty>(
val fixedLeg: FixedLeg<P>,
val floatingLeg: FloatingLeg<P>,
data class State(
val fixedLeg: FixedLeg,
val floatingLeg: FloatingLeg,
val calculation: Calculation,
val common: Common,
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
@ -682,15 +670,15 @@ class InterestRateSwap : Contract {
override val ref = common.tradeID
override val participants: List<PublicKey>
get() = parties.map { it.owningKey }
override val participants: List<AbstractParty>
get() = parties
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return fixedLeg.fixedRatePayer.owningKey.containsAny(ourKeys) || floatingLeg.floatingRatePayer.owningKey.containsAny(ourKeys)
}
override val parties: List<AnonymousParty>
get() = listOf(fixedLeg.fixedRatePayer.toAnonymous(), floatingLeg.floatingRatePayer.toAnonymous())
override val parties: List<AbstractParty>
get() = listOf(fixedLeg.fixedRatePayer, floatingLeg.floatingRatePayer)
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
val nextFixingOf = nextFixingOf() ?: return null
@ -700,10 +688,10 @@ class InterestRateSwap : Contract {
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) {
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? {
@ -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.
*/
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.
* 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 {
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)
val fixedRate = FixedRate(RatioUnit(fixing.value))
tx.addOutputState(

View File

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

View File

@ -79,9 +79,9 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
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 {
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 node2: SimulatedNode = banks[j]
val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State<AnonymousParty>>> = loadLinearHeads(node1)
val theDealRef: StateAndRef<InterestRateSwap.State<AnonymousParty>> = swaps.values.single()
val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State>> = loadLinearHeads(node1)
val theDealRef: StateAndRef<InterestRateSwap.State> = swaps.values.single()
// Do we have any more days left in this deal's lifetime? If not, return.
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
// 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.
val irs = om.readValue<InterestRateSwap.State<AnonymousParty>>(javaClass.classLoader.getResource("simulation/trade.json"))
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity.toAnonymous()
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity.toAnonymous()
val irs = om.readValue<InterestRateSwap.State>(javaClass.classLoader.getResource("simulation/trade.json"))
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity
@InitiatingFlow
class StartDealFlow(val otherParty: Party,

View File

@ -16,12 +16,12 @@ import java.time.LocalDate
import java.util.*
import kotlin.test.assertEquals
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
return when (irsSelect) {
1 -> {
val fixedLeg = InterestRateSwap.FixedLeg(
fixedRatePayer = MEGA_CORP.toAnonymous(),
fixedRatePayer = MEGA_CORP,
notional = 15900000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2016, 3, 10),
@ -40,7 +40,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
)
val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP.toAnonymous(),
floatingRatePayer = MINI_CORP,
notional = 15900000.DOLLARS,
paymentFrequency = Frequency.Quarterly,
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
val fixedLeg = InterestRateSwap.FixedLeg(
fixedRatePayer = MEGA_CORP.toAnonymous(),
fixedRatePayer = MEGA_CORP,
notional = 25000000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2015, 3, 10),
@ -130,7 +130,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
)
val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP.toAnonymous(),
floatingRatePayer = MINI_CORP,
notional = 25000000.DOLLARS,
paymentFrequency = Frequency.Quarterly,
effectiveDate = LocalDate.of(2015, 3, 10),
@ -246,8 +246,8 @@ class IRSTests {
/**
* Utility so I don't have to keep typing this.
*/
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State<AnonymousParty> {
return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State<AnonymousParty>>().single()
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State {
return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single()
}
/**
@ -301,7 +301,7 @@ class IRSTests {
var previousTXN = generateIRSTxn(1)
previousTXN.toLedgerTransaction(services).verify()
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) {
val nextFix: FixOf = currentIRS().nextFixingOf() ?: break
@ -381,7 +381,7 @@ class IRSTests {
transaction("Fix") {
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") {
postAgreement.copy(
postAgreement.fixedLeg,
@ -688,7 +688,7 @@ class IRSTests {
transaction("Fix") {
input("irs post agreement1")
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") {
postAgreement1.copy(
postAgreement1.fixedLeg,
@ -697,7 +697,7 @@ class IRSTests {
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") {
postAgreement2.copy(
postAgreement2.fixedLeg,

View File

@ -13,6 +13,7 @@ import net.corda.core.crypto.generateKeyPair
import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ALICE
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.LogHelper
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())
// Move ownership of the asset to the counterparty
val counterPartyKey = counterpartyNode.owningKey
val asset = issueTx.toWireTransaction().outRef<DummyContract.SingleOwnerState>(0)
val moveTx = DummyContract.move(asset, counterPartyKey).apply {
val moveTx = DummyContract.move(asset, counterpartyNode).apply {
signWith(myKeyPair)
}
// 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.TransactionType
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.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
@ -16,12 +16,12 @@ import java.security.PublicKey
* TODO: Merge with the existing demo IRS code.
*/
data class IRSState(val swap: SwapData,
val buyer: AnonymousParty,
val seller: AnonymousParty,
val buyer: AbstractParty,
val seller: AbstractParty,
override val contract: OGTrade,
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 parties: List<AnonymousParty> get() = listOf(buyer, seller)
override val parties: List<AbstractParty> get() = listOf(buyer, seller)
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
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 }))
}
override val participants: List<PublicKey>
get() = parties.map { it.owningKey }
override val participants: List<AbstractParty>
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(outputs.size == 1) { "" }
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].swap.startDate.isBefore(outputs[0].swap.endDate))
require(outputs[0].swap.notional > BigDecimal(0))

View File

@ -1,10 +1,10 @@
package net.corda.vega.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.flows.FlowLogicRefFactory
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import net.corda.vega.flows.SimmRevaluation
@ -19,7 +19,7 @@ import java.time.temporal.ChronoUnit
*/
data class PortfolioState(val portfolio: List<StateRef>,
override val contract: PortfolioSwap,
private val _parties: Pair<AnonymousParty, AnonymousParty>,
private val _parties: Pair<AbstractParty, AbstractParty>,
val valuationDate: LocalDate,
val valuation: PortfolioValuation? = null,
override val linearId: UniqueIdentifier = UniqueIdentifier())
@ -27,12 +27,12 @@ data class PortfolioState(val portfolio: List<StateRef>,
@CordaSerializable
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()
val valuer: AnonymousParty get() = parties[0]
val valuer: AbstractParty get() = parties[0]
override val participants: List<PublicKey>
get() = parties.map { it.owningKey }
override val participants: List<AbstractParty>
get() = parties
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity {
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 is one output" using (outputs.size == 1)
"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)

View File

@ -1,10 +1,10 @@
package net.corda.vega.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.identity.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
@ -38,7 +38,7 @@ object IRSTradeFlow {
} else {
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")
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 net.corda.core.contracts.StateAndRef
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.InitiatingFlow
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.services.dealsWith
import net.corda.core.serialization.CordaSerializable
@ -85,7 +85,7 @@ object SimmFlow {
@Suspendable
private fun agreePortfolio(portfolio: 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)
send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate))
@ -219,13 +219,6 @@ object SimmFlow {
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
* 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
private fun updateValuation(stateRef: StateAndRef<PortfolioState>) {
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)
subFlow(object : StateRevisionFlow.Receiver<PortfolioState.Update>(replyToParty) {
override fun verifyProposal(proposal: Proposal<PortfolioState.Update>) {

View File

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

View File

@ -1,6 +1,7 @@
package net.corda.vega.flows
import net.corda.core.contracts.StateAndRef
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction
@ -16,7 +17,7 @@ import java.security.PublicKey
object StateRevisionFlow {
class Requester<T>(curStateRef: StateAndRef<RevisionedState<T>>,
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 tx = state.generateRevision(originalState.state.notary, originalState, modification)
tx.setTime(serviceHub.clock.instant(), 30.seconds)

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