mirror of
https://github.com/corda/corda.git
synced 2025-06-14 21:28:14 +00:00
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:
@ -10,6 +10,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
|
|||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.BusinessCalendar
|
import net.corda.core.contracts.BusinessCalendar
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
@ -66,6 +67,7 @@ object JacksonSupport {
|
|||||||
addDeserializer(AnonymousParty::class.java, AnonymousPartyDeserializer)
|
addDeserializer(AnonymousParty::class.java, AnonymousPartyDeserializer)
|
||||||
addSerializer(Party::class.java, PartySerializer)
|
addSerializer(Party::class.java, PartySerializer)
|
||||||
addDeserializer(Party::class.java, PartyDeserializer)
|
addDeserializer(Party::class.java, PartyDeserializer)
|
||||||
|
addDeserializer(AbstractParty::class.java, PartyDeserializer)
|
||||||
addSerializer(BigDecimal::class.java, ToStringSerializer)
|
addSerializer(BigDecimal::class.java, ToStringSerializer)
|
||||||
addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer())
|
addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer())
|
||||||
addSerializer(SecureHash::class.java, SecureHashSerializer)
|
addSerializer(SecureHash::class.java, SecureHashSerializer)
|
||||||
@ -160,8 +162,16 @@ object JacksonSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mapper = parser.codec as PartyObjectMapper
|
val mapper = parser.codec as PartyObjectMapper
|
||||||
|
// TODO: We should probably have a better specified way of identifying X.500 names vs keys
|
||||||
|
// Base58 keys never include an equals character, while X.500 names always will, so we use that to determine
|
||||||
|
// how to parse the content
|
||||||
|
return if (parser.text.contains("=")) {
|
||||||
val principal = X500Name(parser.text)
|
val principal = X500Name(parser.text)
|
||||||
return mapper.partyFromPrincipal(principal) ?: throw JsonParseException(parser, "Could not find a Party with name ${principal}")
|
mapper.partyFromPrincipal(principal) ?: throw JsonParseException(parser, "Could not find a Party with name ${principal}")
|
||||||
|
} else {
|
||||||
|
val key = parsePublicKeyBase58(parser.text)
|
||||||
|
mapper.partyFromKey(key) ?: throw JsonParseException(parser, "Could not find a Party with key ${key.toStringShort()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ inline fun <reified T : MoveCommand> verifyMoveCommand(inputs: List<OwnableState
|
|||||||
// Now check the digital signatures on the move command. Every input has an owning public key, and we must
|
// Now check the digital signatures on the move command. Every input has an owning public key, and we must
|
||||||
// see a signature from each of those keys. The actual signatures have been verified against the transaction
|
// see a signature from each of those keys. The actual signatures have been verified against the transaction
|
||||||
// data by the platform before execution.
|
// data by the platform before execution.
|
||||||
val owningPubKeys = inputs.map { it.owner }.toSet()
|
val owningPubKeys = inputs.map { it.owner.owningKey }.toSet()
|
||||||
val command = commands.requireSingleCommand<T>()
|
val command = commands.requireSingleCommand<T>()
|
||||||
val keysThatSigned = command.signers.toSet()
|
val keysThatSigned = command.signers.toSet()
|
||||||
requireThat {
|
requireThat {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
||||||
|
|
||||||
@ -14,12 +14,12 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
|
|||||||
val magicNumber: Int
|
val magicNumber: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKey) : OwnableState, State {
|
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: AbstractParty) : OwnableState, State {
|
||||||
override val contract = DUMMY_PROGRAM_ID
|
override val contract = DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,9 +28,9 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
|
|||||||
* in a different field, however this is a good example of a contract with multiple states.
|
* in a different field, however this is a good example of a contract with multiple states.
|
||||||
*/
|
*/
|
||||||
data class MultiOwnerState(override val magicNumber: Int = 0,
|
data class MultiOwnerState(override val magicNumber: Int = 0,
|
||||||
val owners: List<PublicKey>) : ContractState, State {
|
val owners: List<AbstractParty>) : ContractState, State {
|
||||||
override val contract = DUMMY_PROGRAM_ID
|
override val contract = DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey> get() = owners
|
override val participants: List<AbstractParty> get() = owners
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
@ -47,22 +47,22 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
|
|||||||
fun generateInitial(magicNumber: Int, notary: Party, owner: PartyAndReference, vararg otherOwners: PartyAndReference): TransactionBuilder {
|
fun generateInitial(magicNumber: Int, notary: Party, owner: PartyAndReference, vararg otherOwners: PartyAndReference): TransactionBuilder {
|
||||||
val owners = listOf(owner) + otherOwners
|
val owners = listOf(owner) + otherOwners
|
||||||
return if (owners.size == 1) {
|
return if (owners.size == 1) {
|
||||||
val state = SingleOwnerState(magicNumber, owners.first().party.owningKey)
|
val state = SingleOwnerState(magicNumber, owners.first().party)
|
||||||
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.first().party.owningKey))
|
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.first().party.owningKey))
|
||||||
} else {
|
} else {
|
||||||
val state = MultiOwnerState(magicNumber, owners.map { it.party.owningKey })
|
val state = MultiOwnerState(magicNumber, owners.map { it.party })
|
||||||
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.map { it.party.owningKey }))
|
TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owners.map { it.party.owningKey }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner)
|
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: AbstractParty) = move(listOf(prior), newOwner)
|
||||||
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder {
|
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: AbstractParty): TransactionBuilder {
|
||||||
require(priors.isNotEmpty())
|
require(priors.isNotEmpty())
|
||||||
val priorState = priors[0].state.data
|
val priorState = priors[0].state.data
|
||||||
val (cmd, state) = priorState.withNewOwner(newOwner)
|
val (cmd, state) = priorState.withNewOwner(newOwner)
|
||||||
return TransactionType.General.Builder(notary = priors[0].state.notary).withItems(
|
return TransactionType.General.Builder(notary = priors[0].state.notary).withItems(
|
||||||
/* INPUTS */ *priors.toTypedArray(),
|
/* INPUTS */ *priors.toTypedArray(),
|
||||||
/* COMMAND */ Command(cmd, priorState.owner),
|
/* COMMAND */ Command(cmd, priorState.owner.owningKey),
|
||||||
/* OUTPUT */ state
|
/* OUTPUT */ state
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.flows.ContractUpgradeFlow
|
import net.corda.flows.ContractUpgradeFlow
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
||||||
val DUMMY_V2_PROGRAM_ID = DummyContractV2()
|
val DUMMY_V2_PROGRAM_ID = DummyContractV2()
|
||||||
@ -15,9 +15,9 @@ val DUMMY_V2_PROGRAM_ID = DummyContractV2()
|
|||||||
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
|
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
|
||||||
override val legacyContract = DummyContract::class.java
|
override val legacyContract = DummyContract::class.java
|
||||||
|
|
||||||
data class State(val magicNumber: Int = 0, val owners: List<PublicKey>) : ContractState {
|
data class State(val magicNumber: Int = 0, val owners: List<AbstractParty>) : ContractState {
|
||||||
override val contract = DUMMY_V2_PROGRAM_ID
|
override val contract = DUMMY_V2_PROGRAM_ID
|
||||||
override val participants: List<PublicKey> = owners
|
override val participants: List<AbstractParty> = owners
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
@ -44,16 +44,16 @@ class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.St
|
|||||||
*
|
*
|
||||||
* @return a pair of wire transaction, and a set of those who should sign the transaction for it to be valid.
|
* @return a pair of wire transaction, and a set of those who should sign the transaction for it to be valid.
|
||||||
*/
|
*/
|
||||||
fun generateUpgradeFromV1(vararg states: StateAndRef<DummyContract.State>): Pair<WireTransaction, Set<PublicKey>> {
|
fun generateUpgradeFromV1(vararg states: StateAndRef<DummyContract.State>): Pair<WireTransaction, Set<AbstractParty>> {
|
||||||
val notary = states.map { it.state.notary }.single()
|
val notary = states.map { it.state.notary }.single()
|
||||||
require(states.isNotEmpty())
|
require(states.isNotEmpty())
|
||||||
|
|
||||||
val signees = states.flatMap { it.state.data.participants }.toSet()
|
val signees: Set<AbstractParty> = states.flatMap { it.state.data.participants }.distinct().toSet()
|
||||||
return Pair(TransactionType.General.Builder(notary).apply {
|
return Pair(TransactionType.General.Builder(notary).apply {
|
||||||
states.forEach {
|
states.forEach {
|
||||||
addInputState(it)
|
addInputState(it)
|
||||||
addOutputState(upgrade(it.state.data))
|
addOutputState(upgrade(it.state.data))
|
||||||
addCommand(UpgradeCommand(DUMMY_V2_PROGRAM_ID.javaClass), signees.toList())
|
addCommand(UpgradeCommand(DUMMY_V2_PROGRAM_ID.javaClass), signees.map { it.owningKey }.toList())
|
||||||
}
|
}
|
||||||
}.toWireTransaction(), signees)
|
}.toWireTransaction(), signees)
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import java.security.PublicKey
|
import net.corda.core.identity.AbstractParty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dummy state for use in testing. Not part of any contract, not even the [DummyContract].
|
* Dummy state for use in testing. Not part of any contract, not even the [DummyContract].
|
||||||
*/
|
*/
|
||||||
data class DummyState(val magicNumber: Int = 0) : ContractState {
|
data class DummyState(val magicNumber: Int = 0) : ContractState {
|
||||||
override val contract = DUMMY_PROGRAM_ID
|
override val contract = DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = emptyList()
|
get() = emptyList()
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.utilities.loggerFor
|
|
||||||
import net.corda.core.utilities.trace
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing")
|
class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing")
|
||||||
|
|
||||||
@ -32,9 +28,9 @@ interface FungibleAsset<T : Any> : OwnableState {
|
|||||||
*/
|
*/
|
||||||
val exitKeys: Collection<PublicKey>
|
val exitKeys: Collection<PublicKey>
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||||
override val owner: PublicKey
|
override val owner: AbstractParty
|
||||||
|
|
||||||
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKey): FungibleAsset<T>
|
fun move(newAmount: Amount<Issued<T>>, newOwner: AbstractParty): FungibleAsset<T>
|
||||||
|
|
||||||
// Just for grouping
|
// Just for grouping
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.core.contracts.clauses.Clause
|
|||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.flows.FlowLogicRef
|
import net.corda.core.flows.FlowLogicRef
|
||||||
import net.corda.core.flows.FlowLogicRefFactory
|
import net.corda.core.flows.FlowLogicRefFactory
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.ServiceType
|
import net.corda.core.node.services.ServiceType
|
||||||
@ -114,7 +115,7 @@ interface ContractState {
|
|||||||
* The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants
|
* The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants
|
||||||
* list should just contain the owner.
|
* list should just contain the owner.
|
||||||
*/
|
*/
|
||||||
val participants: List<PublicKey>
|
val participants: List<AbstractParty>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -174,10 +175,10 @@ fun <T : Any> Amount<Issued<T>>.withoutIssuer(): Amount<T> = Amount(quantity, to
|
|||||||
*/
|
*/
|
||||||
interface OwnableState : ContractState {
|
interface OwnableState : ContractState {
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||||
val owner: PublicKey
|
val owner: AbstractParty
|
||||||
|
|
||||||
/** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */
|
/** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */
|
||||||
fun withNewOwner(newOwner: PublicKey): Pair<CommandData, OwnableState>
|
fun withNewOwner(newOwner: AbstractParty): Pair<CommandData, OwnableState>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Something which is scheduled to happen at a point in time */
|
/** Something which is scheduled to happen at a point in time */
|
||||||
@ -280,7 +281,7 @@ interface DealState : LinearState {
|
|||||||
* separate process exchange certificates to ascertain identities. Thus decoupling identities from
|
* separate process exchange certificates to ascertain identities. Thus decoupling identities from
|
||||||
* [ContractState]s.
|
* [ContractState]s.
|
||||||
* */
|
* */
|
||||||
val parties: List<AnonymousParty>
|
val parties: List<AbstractParty>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a partial transaction representing an agreement (command) to this deal, allowing a general
|
* Generate a partial transaction representing an agreement (command) to this deal, allowing a general
|
||||||
@ -343,9 +344,7 @@ inline fun <reified T : ContractState> Iterable<StateAndRef<ContractState>>.filt
|
|||||||
* ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party.
|
* ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party.
|
||||||
*/
|
*/
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) {
|
data class PartyAndReference(val party: AbstractParty, val reference: OpaqueBytes) {
|
||||||
constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), reference)
|
|
||||||
|
|
||||||
override fun toString() = "$party$reference"
|
override fun toString() = "$party$reference"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ sealed class TransactionType {
|
|||||||
*/
|
*/
|
||||||
class Builder(notary: Party) : TransactionBuilder(NotaryChange, notary) {
|
class Builder(notary: Party) : TransactionBuilder(NotaryChange, notary) {
|
||||||
override fun addInputState(stateAndRef: StateAndRef<*>) {
|
override fun addInputState(stateAndRef: StateAndRef<*>) {
|
||||||
signers.addAll(stateAndRef.state.data.participants)
|
signers.addAll(stateAndRef.state.data.participants.map { it.owningKey })
|
||||||
super.addInputState(stateAndRef)
|
super.addInputState(stateAndRef)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,6 +171,6 @@ sealed class TransactionType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRequiredSigners(tx: LedgerTransaction) = tx.inputs.flatMap { it.state.data.participants }.toSet()
|
override fun getRequiredSigners(tx: LedgerTransaction) = tx.inputs.flatMap { it.state.data.participants }.map { it.owningKey }.toSet()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
package net.corda.core.crypto
|
package net.corda.core.crypto
|
||||||
|
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.serialization.OpaqueBytes
|
import net.corda.core.serialization.OpaqueBytes
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.*
|
import java.security.*
|
||||||
|
|
||||||
@ -18,6 +19,8 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
|
|||||||
override fun toString() = "NULL_KEY"
|
override fun toString() = "NULL_KEY"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val NULL_PARTY = AnonymousParty(NullPublicKey)
|
||||||
|
|
||||||
// TODO: Clean up this duplication between Null and Dummy public key
|
// TODO: Clean up this duplication between Null and Dummy public key
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
|
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
|
||||||
|
@ -16,7 +16,6 @@ abstract class AbstractParty(val owningKey: PublicKey) {
|
|||||||
override fun equals(other: Any?): Boolean = other is AbstractParty && this.owningKey == other.owningKey
|
override fun equals(other: Any?): Boolean = other is AbstractParty && this.owningKey == other.owningKey
|
||||||
|
|
||||||
override fun hashCode(): Int = owningKey.hashCode()
|
override fun hashCode(): Int = owningKey.hashCode()
|
||||||
abstract fun toAnonymous(): AnonymousParty
|
|
||||||
abstract fun nameOrNull(): X500Name?
|
abstract fun nameOrNull(): X500Name?
|
||||||
|
|
||||||
abstract fun ref(bytes: OpaqueBytes): PartyAndReference
|
abstract fun ref(bytes: OpaqueBytes): PartyAndReference
|
||||||
|
@ -18,5 +18,4 @@ class AnonymousParty(owningKey: PublicKey) : AbstractParty(owningKey) {
|
|||||||
override fun nameOrNull(): X500Name? = null
|
override fun nameOrNull(): X500Name? = null
|
||||||
|
|
||||||
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
||||||
override fun toAnonymous() = this
|
|
||||||
}
|
}
|
@ -29,9 +29,8 @@ import java.security.PublicKey
|
|||||||
// TODO: Remove "open" from [Party] once deprecated crypto.Party class is removed
|
// TODO: Remove "open" from [Party] once deprecated crypto.Party class is removed
|
||||||
open class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
|
open class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
|
||||||
constructor(certAndKey: CertificateAndKey) : this(X500Name(certAndKey.certificate.subjectDN.name), certAndKey.keyPair.public)
|
constructor(certAndKey: CertificateAndKey) : this(X500Name(certAndKey.certificate.subjectDN.name), certAndKey.keyPair.public)
|
||||||
override fun toAnonymous(): AnonymousParty = AnonymousParty(owningKey)
|
|
||||||
override fun toString() = "${owningKey.toBase58String()} ($name)"
|
override fun toString() = "${owningKey.toBase58String()} ($name)"
|
||||||
override fun nameOrNull(): X500Name? = name
|
override fun nameOrNull(): X500Name? = name
|
||||||
|
|
||||||
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this.toAnonymous(), bytes)
|
override fun ref(bytes: OpaqueBytes): PartyAndReference = PartyAndReference(this, bytes)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.core.node.services
|
package net.corda.core.node.services
|
||||||
|
|
||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.contracts.PartyAndReference
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -54,7 +55,7 @@ interface IdentityService {
|
|||||||
fun partyFromName(name: String): Party?
|
fun partyFromName(name: String): Party?
|
||||||
fun partyFromX500Name(principal: X500Name): Party?
|
fun partyFromX500Name(principal: X500Name): Party?
|
||||||
|
|
||||||
fun partyFromAnonymous(party: AnonymousParty): Party?
|
fun partyFromAnonymous(party: AbstractParty): Party?
|
||||||
fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,7 +3,9 @@ package net.corda.core.node.services
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.CompositeKey
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -286,7 +288,7 @@ interface VaultService {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
fun generateSpend(tx: TransactionBuilder,
|
fun generateSpend(tx: TransactionBuilder,
|
||||||
amount: Amount<Currency>,
|
amount: Amount<Currency>,
|
||||||
to: PublicKey,
|
to: AbstractParty,
|
||||||
onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<PublicKey>>
|
onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<PublicKey>>
|
||||||
|
|
||||||
// DOCSTART VaultStatesQuery
|
// DOCSTART VaultStatesQuery
|
||||||
|
@ -7,6 +7,7 @@ import net.corda.core.contracts.StateRef
|
|||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -59,13 +60,13 @@ abstract class AbstractStateReplacementFlow {
|
|||||||
|
|
||||||
progressTracker.currentStep = SIGNING
|
progressTracker.currentStep = SIGNING
|
||||||
|
|
||||||
val myKey = serviceHub.myInfo.legalIdentity.owningKey
|
val myKey = serviceHub.myInfo.legalIdentity
|
||||||
val me = listOf(myKey)
|
val me = listOf(myKey)
|
||||||
|
|
||||||
val signatures = if (participants == me) {
|
val signatures = if (participants == me) {
|
||||||
getNotarySignatures(stx)
|
getNotarySignatures(stx)
|
||||||
} else {
|
} else {
|
||||||
collectSignatures(participants - me, stx)
|
collectSignatures((participants - me).map { it.owningKey }, stx)
|
||||||
}
|
}
|
||||||
|
|
||||||
val finalTx = stx + signatures
|
val finalTx = stx + signatures
|
||||||
@ -73,7 +74,7 @@ abstract class AbstractStateReplacementFlow {
|
|||||||
return finalTx.tx.outRef(0)
|
return finalTx.tx.outRef(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>>
|
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>>
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.flows
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -32,12 +33,12 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun verify(input: ContractState, output: ContractState, commandData: Command) {
|
fun verify(input: ContractState, output: ContractState, commandData: Command) {
|
||||||
val command = commandData.value as UpgradeCommand
|
val command = commandData.value as UpgradeCommand
|
||||||
val participants: Set<PublicKey> = input.participants.toSet()
|
val participantKeys: Set<PublicKey> = input.participants.map { it.owningKey }.toSet()
|
||||||
val keysThatSigned: Set<PublicKey> = commandData.signers.toSet()
|
val keysThatSigned: Set<PublicKey> = commandData.signers.toSet()
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *>
|
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *>
|
||||||
requireThat {
|
requireThat {
|
||||||
"The signing keys include all participant keys" using keysThatSigned.containsAll(participants)
|
"The signing keys include all participant keys" using keysThatSigned.containsAll(participantKeys)
|
||||||
"Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract)
|
"Inputs state reference the legacy contract" using (input.contract.javaClass == upgradedContract.legacyContract)
|
||||||
"Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass)
|
"Outputs state reference the upgraded contract" using (output.contract.javaClass == command.upgradedContractClass)
|
||||||
"Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input))
|
"Output state must be an upgraded version of the input state" using (output == upgradedContract.upgrade(input))
|
||||||
@ -53,11 +54,11 @@ class ContractUpgradeFlow<OldState : ContractState, out NewState : ContractState
|
|||||||
.withItems(
|
.withItems(
|
||||||
stateRef,
|
stateRef,
|
||||||
contractUpgrade.upgrade(stateRef.state.data),
|
contractUpgrade.upgrade(stateRef.state.data),
|
||||||
Command(UpgradeCommand(upgradedContractClass), stateRef.state.data.participants))
|
Command(UpgradeCommand(upgradedContractClass), stateRef.state.data.participants.map { it.owningKey }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>> {
|
override fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>> {
|
||||||
val stx = assembleBareTx(originalState, modification)
|
val stx = assembleBareTx(originalState, modification)
|
||||||
.signWith(serviceHub.legalIdentityKey)
|
.signWith(serviceHub.legalIdentityKey)
|
||||||
.toSignedTransaction(false)
|
.toSignedTransaction(false)
|
||||||
|
@ -105,7 +105,7 @@ class FinalityFlow(val transactions: Iterable<SignedTransaction>,
|
|||||||
// Calculate who is meant to see the results based on the participants involved.
|
// Calculate who is meant to see the results based on the participants involved.
|
||||||
val keys = ltx.outputs.flatMap { it.data.participants } + ltx.inputs.flatMap { it.state.data.participants }
|
val keys = ltx.outputs.flatMap { it.data.participants } + ltx.inputs.flatMap { it.state.data.participants }
|
||||||
// TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them count as a reason to fail?
|
// TODO: Is it safe to drop participants we don't know how to contact? Does not knowing how to contact them count as a reason to fail?
|
||||||
val parties = keys.mapNotNull { serviceHub.identityService.partyFromKey(it) }.toSet()
|
val parties = keys.mapNotNull { serviceHub.identityService.partyFromAnonymous(it) }.toSet()
|
||||||
Pair(stx, parties)
|
Pair(stx, parties)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.flows
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
@ -24,11 +25,11 @@ class NotaryChangeFlow<out T : ContractState>(
|
|||||||
progressTracker: ProgressTracker = tracker())
|
progressTracker: ProgressTracker = tracker())
|
||||||
: AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) {
|
: AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) {
|
||||||
|
|
||||||
override fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>> {
|
override fun assembleTx(): Pair<SignedTransaction, Iterable<AbstractParty>> {
|
||||||
val state = originalState.state
|
val state = originalState.state
|
||||||
val tx = TransactionType.NotaryChange.Builder(originalState.state.notary)
|
val tx = TransactionType.NotaryChange.Builder(originalState.state.notary)
|
||||||
|
|
||||||
val participants: Iterable<PublicKey>
|
val participants: Iterable<AbstractParty>
|
||||||
|
|
||||||
if (state.encumbrance == null) {
|
if (state.encumbrance == null) {
|
||||||
val modifiedState = TransactionState(state.data, modification)
|
val modifiedState = TransactionState(state.data, modification)
|
||||||
@ -53,14 +54,14 @@ class NotaryChangeFlow<out T : ContractState>(
|
|||||||
*
|
*
|
||||||
* @return union of all added states' participants
|
* @return union of all added states' participants
|
||||||
*/
|
*/
|
||||||
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<PublicKey> {
|
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<AbstractParty> {
|
||||||
val stateRef = originalState.ref
|
val stateRef = originalState.ref
|
||||||
val txId = stateRef.txhash
|
val txId = stateRef.txhash
|
||||||
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId)
|
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId)
|
||||||
?: throw StateReplacementException("Transaction $txId not found")
|
?: throw StateReplacementException("Transaction $txId not found")
|
||||||
val outputs = issuingTx.tx.outputs
|
val outputs = issuingTx.tx.outputs
|
||||||
|
|
||||||
val participants = mutableSetOf<PublicKey>()
|
val participants = mutableSetOf<AbstractParty>()
|
||||||
|
|
||||||
var nextStateIndex = stateRef.index
|
var nextStateIndex = stateRef.index
|
||||||
var newOutputPosition = tx.outputStates().size
|
var newOutputPosition = tx.outputStates().size
|
||||||
|
Binary file not shown.
@ -1,6 +1,7 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.utilities.ALICE
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.testing.ALICE_PUBKEY
|
import net.corda.testing.ALICE_PUBKEY
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -14,7 +15,7 @@ class DummyContractV2Tests {
|
|||||||
@Test
|
@Test
|
||||||
fun `upgrade from v1`() {
|
fun `upgrade from v1`() {
|
||||||
val contractUpgrade = DummyContractV2()
|
val contractUpgrade = DummyContractV2()
|
||||||
val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), DUMMY_NOTARY)
|
val v1State = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY)
|
||||||
val v1Ref = StateRef(SecureHash.randomSHA256(), 0)
|
val v1Ref = StateRef(SecureHash.randomSHA256(), 0)
|
||||||
val v1StateAndRef = StateAndRef(v1State, v1Ref)
|
val v1StateAndRef = StateAndRef(v1State, v1Ref)
|
||||||
val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef)
|
val (tx, _) = DummyContractV2().generateUpgradeFromV1(v1StateAndRef)
|
||||||
|
@ -2,13 +2,12 @@ package net.corda.core.contracts
|
|||||||
|
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_1
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_2
|
|
||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
|
import net.corda.testing.MINI_CORP
|
||||||
import net.corda.testing.ledger
|
import net.corda.testing.ledger
|
||||||
import net.corda.testing.transaction
|
import net.corda.testing.transaction
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
@ -19,9 +18,9 @@ class TransactionEncumbranceTests {
|
|||||||
|
|
||||||
val state = Cash.State(
|
val state = Cash.State(
|
||||||
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
||||||
owner = DUMMY_PUBKEY_1
|
owner = MEGA_CORP
|
||||||
)
|
)
|
||||||
val stateWithNewOwner = state.copy(owner = DUMMY_PUBKEY_2)
|
val stateWithNewOwner = state.copy(owner = MINI_CORP)
|
||||||
|
|
||||||
val FOUR_PM: Instant = Instant.parse("2015-04-17T16:00:00.00Z")
|
val FOUR_PM: Instant = Instant.parse("2015-04-17T16:00:00.00Z")
|
||||||
val FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS)
|
val FIVE_PM: Instant = FOUR_PM.plus(1, ChronoUnit.HOURS)
|
||||||
@ -40,7 +39,7 @@ class TransactionEncumbranceTests {
|
|||||||
data class State(
|
data class State(
|
||||||
val validFrom: Instant
|
val validFrom: Instant
|
||||||
) : ContractState {
|
) : ContractState {
|
||||||
override val participants: List<PublicKey> = emptyList()
|
override val participants: List<AbstractParty> = emptyList()
|
||||||
override val contract: Contract = TEST_TIMELOCK_ID
|
override val contract: Contract = TEST_TIMELOCK_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +51,7 @@ class TransactionEncumbranceTests {
|
|||||||
input { state }
|
input { state }
|
||||||
output(encumbrance = 1) { stateWithNewOwner }
|
output(encumbrance = 1) { stateWithNewOwner }
|
||||||
output("5pm time-lock") { timeLock }
|
output("5pm time-lock") { timeLock }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||||
verifies()
|
verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +69,7 @@ class TransactionEncumbranceTests {
|
|||||||
input("state encumbered by 5pm time-lock")
|
input("state encumbered by 5pm time-lock")
|
||||||
input("5pm time-lock")
|
input("5pm time-lock")
|
||||||
output { stateWithNewOwner }
|
output { stateWithNewOwner }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||||
timestamp(FIVE_PM)
|
timestamp(FIVE_PM)
|
||||||
verifies()
|
verifies()
|
||||||
}
|
}
|
||||||
@ -89,7 +88,7 @@ class TransactionEncumbranceTests {
|
|||||||
input("state encumbered by 5pm time-lock")
|
input("state encumbered by 5pm time-lock")
|
||||||
input("5pm time-lock")
|
input("5pm time-lock")
|
||||||
output { state }
|
output { state }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||||
timestamp(FOUR_PM)
|
timestamp(FOUR_PM)
|
||||||
this `fails with` "the time specified in the time-lock has passed"
|
this `fails with` "the time specified in the time-lock has passed"
|
||||||
}
|
}
|
||||||
@ -106,7 +105,7 @@ class TransactionEncumbranceTests {
|
|||||||
transaction {
|
transaction {
|
||||||
input("state encumbered by 5pm time-lock")
|
input("state encumbered by 5pm time-lock")
|
||||||
output { stateWithNewOwner }
|
output { stateWithNewOwner }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||||
timestamp(FIVE_PM)
|
timestamp(FIVE_PM)
|
||||||
this `fails with` "Missing required encumbrance 1 in INPUT"
|
this `fails with` "Missing required encumbrance 1 in INPUT"
|
||||||
}
|
}
|
||||||
@ -118,7 +117,7 @@ class TransactionEncumbranceTests {
|
|||||||
transaction {
|
transaction {
|
||||||
input { state }
|
input { state }
|
||||||
output(encumbrance = 0) { stateWithNewOwner }
|
output(encumbrance = 0) { stateWithNewOwner }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||||
this `fails with` "Missing required encumbrance 0 in OUTPUT"
|
this `fails with` "Missing required encumbrance 0 in OUTPUT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,7 +128,7 @@ class TransactionEncumbranceTests {
|
|||||||
input { state }
|
input { state }
|
||||||
output(encumbrance = 2) { stateWithNewOwner }
|
output(encumbrance = 2) { stateWithNewOwner }
|
||||||
output { timeLock }
|
output { timeLock }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||||
this `fails with` "Missing required encumbrance 2 in OUTPUT"
|
this `fails with` "Missing required encumbrance 2 in OUTPUT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +145,7 @@ class TransactionEncumbranceTests {
|
|||||||
input("state encumbered by some other state")
|
input("state encumbered by some other state")
|
||||||
input("5pm time-lock")
|
input("5pm time-lock")
|
||||||
output { stateWithNewOwner }
|
output { stateWithNewOwner }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(MEGA_CORP.owningKey) { Cash.Commands.Move() }
|
||||||
timestamp(FIVE_PM)
|
timestamp(FIVE_PM)
|
||||||
this `fails with` "Missing required encumbrance 1 in INPUT"
|
this `fails with` "Missing required encumbrance 1 in INPUT"
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import net.corda.core.transactions.LedgerTransaction
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
import net.corda.testing.ALICE_PUBKEY
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -95,7 +94,7 @@ class TransactionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `transactions with no inputs can have any notary`() {
|
fun `transactions with no inputs can have any notary`() {
|
||||||
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), DUMMY_NOTARY)
|
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY)
|
||||||
val inputs = emptyList<StateAndRef<*>>()
|
val inputs = emptyList<StateAndRef<*>>()
|
||||||
val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB))
|
val outputs = listOf(baseOutState, baseOutState.copy(notary = ALICE), baseOutState.copy(notary = BOB))
|
||||||
val commands = emptyList<AuthenticatedObject<CommandData>>()
|
val commands = emptyList<AuthenticatedObject<CommandData>>()
|
||||||
@ -120,7 +119,7 @@ class TransactionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `transaction verification fails for duplicate inputs`() {
|
fun `transaction verification fails for duplicate inputs`() {
|
||||||
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), DUMMY_NOTARY)
|
val baseOutState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), DUMMY_NOTARY)
|
||||||
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
||||||
val stateAndRef = StateAndRef(baseOutState, stateRef)
|
val stateAndRef = StateAndRef(baseOutState, stateRef)
|
||||||
val inputs = listOf(stateAndRef, stateAndRef)
|
val inputs = listOf(stateAndRef, stateAndRef)
|
||||||
@ -148,7 +147,7 @@ class TransactionTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `general transactions cannot change notary`() {
|
fun `general transactions cannot change notary`() {
|
||||||
val notary: Party = DUMMY_NOTARY
|
val notary: Party = DUMMY_NOTARY
|
||||||
val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE_PUBKEY), notary)
|
val inState = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary)
|
||||||
val outState = inState.copy(notary = ALICE)
|
val outState = inState.copy(notary = ALICE)
|
||||||
val inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0)))
|
val inputs = listOf(StateAndRef(inState, StateRef(SecureHash.randomSHA256(), 0)))
|
||||||
val outputs = listOf(outState)
|
val outputs = listOf(outState)
|
||||||
|
@ -12,9 +12,7 @@ import net.corda.core.transactions.WireTransaction
|
|||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_1
|
import net.corda.core.utilities.DUMMY_PUBKEY_1
|
||||||
import net.corda.core.utilities.TEST_TX_TIME
|
import net.corda.core.utilities.TEST_TX_TIME
|
||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.*
|
||||||
import net.corda.testing.MEGA_CORP_PUBKEY
|
|
||||||
import net.corda.testing.ledger
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
@ -30,20 +28,20 @@ class PartialMerkleTreeTest {
|
|||||||
output("MEGA_CORP cash") {
|
output("MEGA_CORP cash") {
|
||||||
Cash.State(
|
Cash.State(
|
||||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||||
owner = MEGA_CORP_PUBKEY
|
owner = MEGA_CORP
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
output("dummy cash 1") {
|
output("dummy cash 1") {
|
||||||
Cash.State(
|
Cash.State(
|
||||||
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
amount = 900.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||||
owner = DUMMY_PUBKEY_1
|
owner = MINI_CORP
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
input("MEGA_CORP cash")
|
input("MEGA_CORP cash")
|
||||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
|
output("MEGA_CORP cash".output<Cash.State>().copy(owner = MINI_CORP))
|
||||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -98,7 +96,7 @@ class PartialMerkleTreeTest {
|
|||||||
fun filtering(elem: Any): Boolean {
|
fun filtering(elem: Any): Boolean {
|
||||||
return when (elem) {
|
return when (elem) {
|
||||||
is StateRef -> true
|
is StateRef -> true
|
||||||
is TransactionState<*> -> elem.data.participants[0].keys == DUMMY_PUBKEY_1.keys
|
is TransactionState<*> -> elem.data.participants[0].owningKey.keys == MINI_CORP_PUBKEY.keys
|
||||||
is Command -> MEGA_CORP_PUBKEY in elem.signers
|
is Command -> MEGA_CORP_PUBKEY in elem.signers
|
||||||
is Timestamp -> true
|
is Timestamp -> true
|
||||||
is PublicKey -> elem == MEGA_CORP_PUBKEY
|
is PublicKey -> elem == MEGA_CORP_PUBKEY
|
||||||
|
@ -88,7 +88,7 @@ class CollectSignaturesFlowTests {
|
|||||||
val state = receive<DummyContract.MultiOwnerState>(otherParty).unwrap { it }
|
val state = receive<DummyContract.MultiOwnerState>(otherParty).unwrap { it }
|
||||||
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
|
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
|
||||||
|
|
||||||
val command = Command(DummyContract.Commands.Create(), state.participants)
|
val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey })
|
||||||
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
|
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
|
||||||
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
|
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
|
||||||
val stx = subFlow(CollectSignaturesFlow(ptx))
|
val stx = subFlow(CollectSignaturesFlow(ptx))
|
||||||
@ -108,7 +108,7 @@ class CollectSignaturesFlowTests {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
override fun call(): SignedTransaction {
|
override fun call(): SignedTransaction {
|
||||||
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
|
val notary = serviceHub.networkMapCache.notaryNodes.single().notaryIdentity
|
||||||
val command = Command(DummyContract.Commands.Create(), state.participants)
|
val command = Command(DummyContract.Commands.Create(), state.participants.map { it.owningKey })
|
||||||
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
|
val builder = TransactionType.General.Builder(notary = notary).withItems(state, command)
|
||||||
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
|
val ptx = builder.signWith(serviceHub.legalIdentityKey).toSignedTransaction(false)
|
||||||
val stx = subFlow(CollectSignaturesFlow(ptx))
|
val stx = subFlow(CollectSignaturesFlow(ptx))
|
||||||
@ -142,7 +142,7 @@ class CollectSignaturesFlowTests {
|
|||||||
fun `successfully collects two signatures`() {
|
fun `successfully collects two signatures`() {
|
||||||
val magicNumber = 1337
|
val magicNumber = 1337
|
||||||
val parties = listOf(a.info.legalIdentity, b.info.legalIdentity, c.info.legalIdentity)
|
val parties = listOf(a.info.legalIdentity, b.info.legalIdentity, c.info.legalIdentity)
|
||||||
val state = DummyContract.MultiOwnerState(magicNumber, parties.map { it.owningKey })
|
val state = DummyContract.MultiOwnerState(magicNumber, parties)
|
||||||
val flow = a.services.startFlow(TestFlowTwo.Initiator(state, b.info.legalIdentity))
|
val flow = a.services.startFlow(TestFlowTwo.Initiator(state, b.info.legalIdentity))
|
||||||
mockNet.runNetwork()
|
mockNet.runNetwork()
|
||||||
val result = flow.resultFuture.getOrThrow()
|
val result = flow.resultFuture.getOrThrow()
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.startFlow
|
import net.corda.core.messaging.startFlow
|
||||||
@ -27,7 +28,6 @@ import net.corda.testing.startRpcClient
|
|||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
@ -187,21 +187,21 @@ class ContractUpgradeFlowTest {
|
|||||||
val firstState = a.database.transaction { a.vault.unconsumedStates<ContractState>().single() }
|
val firstState = a.database.transaction { a.vault.unconsumedStates<ContractState>().single() }
|
||||||
assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.")
|
assertTrue(firstState.state.data is CashV2.State, "Contract state is upgraded to the new version.")
|
||||||
assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.")
|
assertEquals(Amount(1000000, USD).`issued by`(a.info.legalIdentity.ref(1)), (firstState.state.data as CashV2.State).amount, "Upgraded cash contain the correct amount.")
|
||||||
assertEquals(listOf(a.info.legalIdentity.owningKey), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")
|
assertEquals<Collection<AbstractParty>>(listOf(a.info.legalIdentity), (firstState.state.data as CashV2.State).owners, "Upgraded cash belongs to the right owner.")
|
||||||
}
|
}
|
||||||
|
|
||||||
class CashV2 : UpgradedContract<Cash.State, CashV2.State> {
|
class CashV2 : UpgradedContract<Cash.State, CashV2.State> {
|
||||||
override val legacyContract = Cash::class.java
|
override val legacyContract = Cash::class.java
|
||||||
|
|
||||||
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<PublicKey>) : FungibleAsset<Currency> {
|
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<AbstractParty>) : FungibleAsset<Currency> {
|
||||||
override val owner: PublicKey = owners.first()
|
override val owner: AbstractParty = owners.first()
|
||||||
override val exitKeys = (owners + amount.token.issuer.party.owningKey).toSet()
|
override val exitKeys = (owners + amount.token.issuer.party).map { it.owningKey }.toSet()
|
||||||
override val contract = CashV2()
|
override val contract = CashV2()
|
||||||
override val participants = owners
|
override val participants = owners
|
||||||
|
|
||||||
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKey) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
|
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
|
||||||
override fun toString() = "${Emoji.bagOfCash}New Cash($amount at ${amount.token.issuer} owned by $owner)"
|
override fun toString() = "${Emoji.bagOfCash}New Cash($amount at ${amount.token.issuer} owned by $owner)"
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Cash.Commands.Move(), copy(owners = listOf(newOwner)))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Cash.Commands.Move(), copy(owners = listOf(newOwner)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner))
|
override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner))
|
||||||
|
@ -12,7 +12,7 @@ import net.corda.flows.ResolveTransactionsFlow
|
|||||||
import net.corda.node.utilities.transaction
|
import net.corda.node.utilities.transaction
|
||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
import net.corda.testing.MEGA_CORP_KEY
|
import net.corda.testing.MEGA_CORP_KEY
|
||||||
import net.corda.testing.MINI_CORP_PUBKEY
|
import net.corda.testing.MINI_CORP
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -94,7 +94,7 @@ class ResolveTransactionsFlowTest {
|
|||||||
val count = 50
|
val count = 50
|
||||||
var cursor = stx2
|
var cursor = stx2
|
||||||
repeat(count) {
|
repeat(count) {
|
||||||
val stx = DummyContract.move(cursor.tx.outRef(0), MINI_CORP_PUBKEY)
|
val stx = DummyContract.move(cursor.tx.outRef(0), MINI_CORP)
|
||||||
.addSignatureUnchecked(NullSignature)
|
.addSignatureUnchecked(NullSignature)
|
||||||
.toSignedTransaction(false)
|
.toSignedTransaction(false)
|
||||||
a.database.transaction {
|
a.database.transaction {
|
||||||
@ -113,13 +113,13 @@ class ResolveTransactionsFlowTest {
|
|||||||
fun `triangle of transactions resolves fine`() {
|
fun `triangle of transactions resolves fine`() {
|
||||||
val stx1 = makeTransactions().first
|
val stx1 = makeTransactions().first
|
||||||
|
|
||||||
val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP_PUBKEY).run {
|
val stx2 = DummyContract.move(stx1.tx.outRef(0), MINI_CORP).run {
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
toSignedTransaction()
|
toSignedTransaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP_PUBKEY).run {
|
val stx3 = DummyContract.move(listOf(stx1.tx.outRef(0), stx2.tx.outRef(0)), MINI_CORP).run {
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
toSignedTransaction()
|
toSignedTransaction()
|
||||||
@ -173,7 +173,7 @@ class ResolveTransactionsFlowTest {
|
|||||||
it.signWith(DUMMY_NOTARY_KEY)
|
it.signWith(DUMMY_NOTARY_KEY)
|
||||||
it.toSignedTransaction(false)
|
it.toSignedTransaction(false)
|
||||||
}
|
}
|
||||||
val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP_PUBKEY).let {
|
val dummy2: SignedTransaction = DummyContract.move(dummy1.tx.outRef(0), MINI_CORP).let {
|
||||||
it.signWith(MEGA_CORP_KEY)
|
it.signWith(MEGA_CORP_KEY)
|
||||||
it.signWith(DUMMY_NOTARY_KEY)
|
it.signWith(DUMMY_NOTARY_KEY)
|
||||||
it.toSignedTransaction()
|
it.toSignedTransaction()
|
||||||
|
@ -5,6 +5,7 @@ import com.nhaarman.mockito_kotlin.mock
|
|||||||
import com.nhaarman.mockito_kotlin.whenever
|
import com.nhaarman.mockito_kotlin.whenever
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.AttachmentStorage
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
import net.corda.core.node.services.StorageService
|
import net.corda.core.node.services.StorageService
|
||||||
@ -53,7 +54,7 @@ class AttachmentClassLoaderTests {
|
|||||||
class AttachmentDummyContract : Contract {
|
class AttachmentDummyContract : Contract {
|
||||||
data class State(val magicNumber: Int = 0) : ContractState {
|
data class State(val magicNumber: Int = 0) : ContractState {
|
||||||
override val contract = ATTACHMENT_TEST_PROGRAM_ID
|
override val contract = ATTACHMENT_TEST_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = listOf()
|
get() = listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ package net.corda.core.node
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class VaultUpdateTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class DummyState : ContractState {
|
private class DummyState : ContractState {
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = emptyList()
|
get() = emptyList()
|
||||||
override val contract = VaultUpdateTests.DummyContract
|
override val contract = VaultUpdateTests.DummyContract
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,19 @@ package net.corda.core.serialization
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.seconds
|
import net.corda.core.seconds
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.DUMMY_KEY_2
|
||||||
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
|
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
||||||
|
import net.corda.core.utilities.TEST_TX_TIME
|
||||||
|
import net.corda.testing.MEGA_CORP
|
||||||
|
import net.corda.testing.MEGA_CORP_KEY
|
||||||
import net.corda.testing.MINI_CORP
|
import net.corda.testing.MINI_CORP
|
||||||
import net.corda.testing.generateStateRef
|
import net.corda.testing.generateStateRef
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -27,12 +32,12 @@ class TransactionSerializationTests {
|
|||||||
data class State(
|
data class State(
|
||||||
val deposit: PartyAndReference,
|
val deposit: PartyAndReference,
|
||||||
val amount: Amount<Currency>,
|
val amount: Amount<Currency>,
|
||||||
override val owner: PublicKey) : OwnableState {
|
override val owner: AbstractParty) : OwnableState {
|
||||||
override val contract: Contract = TEST_PROGRAM_ID
|
override val contract: Contract = TEST_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
@ -44,9 +49,9 @@ class TransactionSerializationTests {
|
|||||||
// It refers to a fake TX/state that we don't bother creating here.
|
// It refers to a fake TX/state that we don't bother creating here.
|
||||||
val depositRef = MINI_CORP.ref(1)
|
val depositRef = MINI_CORP.ref(1)
|
||||||
val fakeStateRef = generateStateRef()
|
val fakeStateRef = generateStateRef()
|
||||||
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY), fakeStateRef)
|
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, MEGA_CORP), DUMMY_NOTARY), fakeStateRef)
|
||||||
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY)
|
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, MEGA_CORP), DUMMY_NOTARY)
|
||||||
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public), DUMMY_NOTARY)
|
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, MEGA_CORP), DUMMY_NOTARY)
|
||||||
|
|
||||||
|
|
||||||
lateinit var tx: TransactionBuilder
|
lateinit var tx: TransactionBuilder
|
||||||
@ -54,14 +59,14 @@ class TransactionSerializationTests {
|
|||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems(
|
tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems(
|
||||||
inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(DUMMY_KEY_1.public))
|
inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(MEGA_CORP.owningKey))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun signWireTX() {
|
fun signWireTX() {
|
||||||
tx.signWith(DUMMY_NOTARY_KEY)
|
tx.signWith(DUMMY_NOTARY_KEY)
|
||||||
tx.signWith(DUMMY_KEY_1)
|
tx.signWith(MEGA_CORP_KEY)
|
||||||
val signedTX = tx.toSignedTransaction()
|
val signedTX = tx.toSignedTransaction()
|
||||||
|
|
||||||
// Now check that the signature we just made verifies.
|
// Now check that the signature we just made verifies.
|
||||||
@ -81,7 +86,7 @@ class TransactionSerializationTests {
|
|||||||
tx.toSignedTransaction()
|
tx.toSignedTransaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.signWith(DUMMY_KEY_1)
|
tx.signWith(MEGA_CORP_KEY)
|
||||||
tx.signWith(DUMMY_NOTARY_KEY)
|
tx.signWith(DUMMY_NOTARY_KEY)
|
||||||
val signedTX = tx.toSignedTransaction()
|
val signedTX = tx.toSignedTransaction()
|
||||||
|
|
||||||
@ -104,7 +109,7 @@ class TransactionSerializationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun timestamp() {
|
fun timestamp() {
|
||||||
tx.setTime(TEST_TX_TIME, 30.seconds)
|
tx.setTime(TEST_TX_TIME, 30.seconds)
|
||||||
tx.signWith(DUMMY_KEY_1)
|
tx.signWith(MEGA_CORP_KEY)
|
||||||
tx.signWith(DUMMY_NOTARY_KEY)
|
tx.signWith(DUMMY_NOTARY_KEY)
|
||||||
val stx = tx.toSignedTransaction()
|
val stx = tx.toSignedTransaction()
|
||||||
assertEquals(TEST_TX_TIME, stx.tx.timestamp?.midpoint)
|
assertEquals(TEST_TX_TIME, stx.tx.timestamp?.midpoint)
|
||||||
|
@ -38,10 +38,9 @@ UNRELEASED
|
|||||||
* ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for
|
* ``Party`` has moved to the ``net.corda.core.identity`` package; there is a deprecated class in its place for
|
||||||
backwards compatibility, but it will be removed in a future release and developers should move to the new class as soon
|
backwards compatibility, but it will be removed in a future release and developers should move to the new class as soon
|
||||||
as possible.
|
as possible.
|
||||||
* There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. A new class
|
* There is a new ``AbstractParty`` superclass to ``Party``, which contains just the public key. This now replaces
|
||||||
``AnonymousParty`` has been added, which is intended to be used in place of ``Party`` or ``PublicKey`` in contract
|
use of ``Party`` and ``PublicKey`` in state objects, and allows use of full or anonymised parties depending on
|
||||||
state objects. The exception to this is where the party in a contract state is intended to be well known, such as
|
use-case.
|
||||||
issuer of a ``Cash`` state.
|
|
||||||
* Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the
|
* Names of parties are now stored as a ``X500Name`` rather than a ``String``, to correctly enforce basic structure of the
|
||||||
name. As a result all node legal names must now be structured as X.500 distinguished names.
|
name. As a result all node legal names must now be structured as X.500 distinguished names.
|
||||||
|
|
||||||
|
@ -48,12 +48,12 @@ private fun gatherOurInputs(serviceHub: ServiceHub,
|
|||||||
notary: Party?): Pair<List<StateAndRef<Cash.State>>, Long> {
|
notary: Party?): Pair<List<StateAndRef<Cash.State>>, Long> {
|
||||||
// Collect cash type inputs
|
// Collect cash type inputs
|
||||||
val cashStates = serviceHub.vaultService.unconsumedStates<Cash.State>()
|
val cashStates = serviceHub.vaultService.unconsumedStates<Cash.State>()
|
||||||
// extract our key identity for convenience
|
// extract our identity for convenience
|
||||||
val ourKey = serviceHub.myInfo.legalIdentity.owningKey
|
val ourIdentity = serviceHub.myInfo.legalIdentity
|
||||||
// Filter down to our own cash states with right currency and issuer
|
// Filter down to our own cash states with right currency and issuer
|
||||||
val suitableCashStates = cashStates.filter {
|
val suitableCashStates = cashStates.filter {
|
||||||
val state = it.state.data
|
val state = it.state.data
|
||||||
(state.owner == ourKey)
|
(state.owner == ourIdentity)
|
||||||
&& (state.amount.token == amountRequired.token)
|
&& (state.amount.token == amountRequired.token)
|
||||||
}
|
}
|
||||||
require(!suitableCashStates.isEmpty()) { "Insufficient funds" }
|
require(!suitableCashStates.isEmpty()) { "Insufficient funds" }
|
||||||
@ -90,12 +90,12 @@ private fun prepareOurInputsAndOutputs(serviceHub: ServiceHub, request: FxReques
|
|||||||
val (inputs, residual) = gatherOurInputs(serviceHub, sellAmount, request.notary)
|
val (inputs, residual) = gatherOurInputs(serviceHub, sellAmount, request.notary)
|
||||||
|
|
||||||
// Build and an output state for the counterparty
|
// Build and an output state for the counterparty
|
||||||
val transferedFundsOutput = Cash.State(sellAmount, request.counterparty.owningKey)
|
val transferedFundsOutput = Cash.State(sellAmount, request.counterparty)
|
||||||
|
|
||||||
if (residual > 0L) {
|
if (residual > 0L) {
|
||||||
// Build an output state for the residual change back to us
|
// Build an output state for the residual change back to us
|
||||||
val residualAmount = Amount(residual, sellAmount.token)
|
val residualAmount = Amount(residual, sellAmount.token)
|
||||||
val residualOutput = Cash.State(residualAmount, serviceHub.myInfo.legalIdentity.owningKey)
|
val residualOutput = Cash.State(residualAmount, serviceHub.myInfo.legalIdentity)
|
||||||
return FxResponse(inputs, listOf(transferedFundsOutput, residualOutput))
|
return FxResponse(inputs, listOf(transferedFundsOutput, residualOutput))
|
||||||
} else {
|
} else {
|
||||||
return FxResponse(inputs, listOf(transferedFundsOutput))
|
return FxResponse(inputs, listOf(transferedFundsOutput))
|
||||||
@ -140,7 +140,7 @@ class ForeignExchangeFlow(val tradeId: String,
|
|||||||
require(it.inputs.all { it.state.notary == notary }) {
|
require(it.inputs.all { it.state.notary == notary }) {
|
||||||
"notary of remote states must be same as for our states"
|
"notary of remote states must be same as for our states"
|
||||||
}
|
}
|
||||||
require(it.inputs.all { it.state.data.owner == remoteRequestWithNotary.owner.owningKey }) {
|
require(it.inputs.all { it.state.data.owner == remoteRequestWithNotary.owner }) {
|
||||||
"The inputs are not owned by the correct counterparty"
|
"The inputs are not owned by the correct counterparty"
|
||||||
}
|
}
|
||||||
require(it.inputs.all { it.state.data.amount.token == remoteRequestWithNotary.amount.token }) {
|
require(it.inputs.all { it.state.data.amount.token == remoteRequestWithNotary.amount.token }) {
|
||||||
@ -153,7 +153,7 @@ class ForeignExchangeFlow(val tradeId: String,
|
|||||||
>= remoteRequestWithNotary.amount.quantity) {
|
>= remoteRequestWithNotary.amount.quantity) {
|
||||||
"the provided inputs don't provide sufficient funds"
|
"the provided inputs don't provide sufficient funds"
|
||||||
}
|
}
|
||||||
require(it.outputs.filter { it.owner == serviceHub.myInfo.legalIdentity.owningKey }.
|
require(it.outputs.filter { it.owner == serviceHub.myInfo.legalIdentity }.
|
||||||
map { it.amount.quantity }.sum() == remoteRequestWithNotary.amount.quantity) {
|
map { it.amount.quantity }.sum() == remoteRequestWithNotary.amount.quantity) {
|
||||||
"the provided outputs don't provide the request quantity"
|
"the provided outputs don't provide the request quantity"
|
||||||
}
|
}
|
||||||
@ -195,8 +195,8 @@ class ForeignExchangeFlow(val tradeId: String,
|
|||||||
val builder = TransactionType.General.Builder(ourStates.inputs.first().state.notary)
|
val builder = TransactionType.General.Builder(ourStates.inputs.first().state.notary)
|
||||||
|
|
||||||
// Add the move commands and key to indicate all the respective owners and need to sign
|
// Add the move commands and key to indicate all the respective owners and need to sign
|
||||||
val ourSigners = ourStates.inputs.map { it.state.data.owner }.toSet()
|
val ourSigners = ourStates.inputs.map { it.state.data.owner.owningKey }.toSet()
|
||||||
val theirSigners = theirStates.inputs.map { it.state.data.owner }.toSet()
|
val theirSigners = theirStates.inputs.map { it.state.data.owner.owningKey }.toSet()
|
||||||
builder.addCommand(Cash.Commands.Move(), (ourSigners + theirSigners).toList())
|
builder.addCommand(Cash.Commands.Move(), (ourSigners + theirSigners).toList())
|
||||||
|
|
||||||
// Build and add the inputs and outputs
|
// Build and add the inputs and outputs
|
||||||
|
@ -2,9 +2,13 @@ package net.corda.docs
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.containsAny
|
||||||
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.PluginServiceHub
|
import net.corda.core.node.PluginServiceHub
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
@ -64,10 +68,10 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
|
|||||||
override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState {
|
override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState {
|
||||||
|
|
||||||
val parties: List<Party> get() = listOf(source, counterparty)
|
val parties: List<Party> get() = listOf(source, counterparty)
|
||||||
override val participants: List<PublicKey> get() = parties.map { it.owningKey }
|
override val participants: List<AbstractParty> get() = parties
|
||||||
|
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return participants.any { it.containsAny(ourKeys) }
|
return participants.any { it.owningKey.containsAny(ourKeys) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@ We've added the ability for flows to be versioned by their CorDapp developers. T
|
|||||||
version of a flow and allows it to reject flow communication with a node which isn't using the same fact. In a future
|
version of a flow and allows it to reject flow communication with a node which isn't using the same fact. In a future
|
||||||
release we allow a node to have multiple versions of the same flow running to enable backwards compatibility.
|
release we allow a node to have multiple versions of the same flow running to enable backwards compatibility.
|
||||||
|
|
||||||
There are major changes to the ``Party`` class as part of confidential identities. See :doc:`changelog` for full details.
|
There are major changes to the ``Party`` class as part of confidential identities, and how parties and keys are stored
|
||||||
|
in transaction state objects. See :doc:`changelog` for full details.
|
||||||
|
|
||||||
|
|
||||||
Milestone 11
|
Milestone 11
|
||||||
|
@ -2,16 +2,16 @@ package net.corda.contracts.universal
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
val UNIVERSAL_PROGRAM_ID = UniversalContract()
|
val UNIVERSAL_PROGRAM_ID = UniversalContract()
|
||||||
|
|
||||||
class UniversalContract : Contract {
|
class UniversalContract : Contract {
|
||||||
data class State(override val participants: List<PublicKey>,
|
data class State(override val participants: List<AbstractParty>,
|
||||||
val details: Arrangement) : ContractState {
|
val details: Arrangement) : ContractState {
|
||||||
override val contract = UNIVERSAL_PROGRAM_ID
|
override val contract = UNIVERSAL_PROGRAM_ID
|
||||||
}
|
}
|
||||||
@ -316,7 +316,7 @@ class UniversalContract : Contract {
|
|||||||
override val legalContractReference: SecureHash
|
override val legalContractReference: SecureHash
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKey) {
|
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: Party) {
|
||||||
check(tx.inputStates().isEmpty())
|
check(tx.inputStates().isEmpty())
|
||||||
tx.addOutputState(State(listOf(notary), arrangement))
|
tx.addOutputState(State(listOf(notary), arrangement))
|
||||||
tx.addCommand(Commands.Issue(), at.party.owningKey)
|
tx.addCommand(Commands.Issue(), at.party.owningKey)
|
||||||
|
@ -130,15 +130,15 @@ class Cap {
|
|||||||
val paymentFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
val paymentFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
||||||
|
|
||||||
|
|
||||||
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial)
|
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
|
||||||
|
|
||||||
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFirst)
|
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFirst)
|
||||||
|
|
||||||
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterExecutionFirst)
|
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterExecutionFirst)
|
||||||
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst)
|
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFirst)
|
||||||
|
|
||||||
val stateAfterFixingFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFinal)
|
val stateAfterFixingFinal = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFinal)
|
||||||
val statePaymentFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFinal)
|
val statePaymentFinal = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFinal)
|
||||||
|
|
||||||
val contractLimitedCap = arrange {
|
val contractLimitedCap = arrange {
|
||||||
rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.SemiAnnual, object {
|
rollOut("2016-04-01".ld, "2017-04-01".ld, Frequency.SemiAnnual, object {
|
||||||
|
@ -44,11 +44,11 @@ class Caplet {
|
|||||||
|
|
||||||
val contractFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
val contractFinal = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
||||||
|
|
||||||
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
|
||||||
|
|
||||||
val stateFixed = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractFixed)
|
val stateFixed = UniversalContract.State(listOf(DUMMY_NOTARY), contractFixed)
|
||||||
|
|
||||||
val stateFinal = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractFinal)
|
val stateFinal = UniversalContract.State(listOf(DUMMY_NOTARY), contractFinal)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun issue() {
|
fun issue() {
|
||||||
|
@ -43,9 +43,9 @@ class FXFwdTimeOption
|
|||||||
val TEST_TX_TIME_BEFORE_MATURITY: Instant get() = Instant.parse("2018-05-01T12:00:00.00Z")
|
val TEST_TX_TIME_BEFORE_MATURITY: Instant get() = Instant.parse("2018-05-01T12:00:00.00Z")
|
||||||
val TEST_TX_TIME_AFTER_MATURITY: Instant get() = Instant.parse("2018-06-02T12:00:00.00Z")
|
val TEST_TX_TIME_AFTER_MATURITY: Instant get() = Instant.parse("2018-06-02T12:00:00.00Z")
|
||||||
|
|
||||||
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), initialContract)
|
val inState = UniversalContract.State(listOf(DUMMY_NOTARY), initialContract)
|
||||||
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract1)
|
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY), outContract1)
|
||||||
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), outContract2)
|
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY), outContract2)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `issue - signature`() {
|
fun `issue - signature`() {
|
||||||
|
@ -25,18 +25,18 @@ class FXSwap {
|
|||||||
val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, EUR) }
|
val transfer1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, EUR) }
|
||||||
val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, USD) }
|
val transfer2 = arrange { acmeCorp.owes(highStreetBank, 1.M, USD) }
|
||||||
|
|
||||||
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer1)
|
val outState1 = UniversalContract.State(listOf(DUMMY_NOTARY), transfer1)
|
||||||
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer2)
|
val outState2 = UniversalContract.State(listOf(DUMMY_NOTARY), transfer2)
|
||||||
|
|
||||||
val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, USD) } // wrong currency
|
val transferBad1 = arrange { highStreetBank.owes(acmeCorp, 1070.K, USD) } // wrong currency
|
||||||
val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, USD) } // wrong amount
|
val transferBad2 = arrange { acmeCorp.owes(highStreetBank, 900.K, USD) } // wrong amount
|
||||||
val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1070.K, EUR) } // wrong party
|
val transferBad3 = arrange { highStreetBank.owes(highStreetBank, 1070.K, EUR) } // wrong party
|
||||||
|
|
||||||
val outStateBad1 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad1)
|
val outStateBad1 = UniversalContract.State(listOf(DUMMY_NOTARY), transferBad1)
|
||||||
val outStateBad2 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad2)
|
val outStateBad2 = UniversalContract.State(listOf(DUMMY_NOTARY), transferBad2)
|
||||||
val outStateBad3 = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferBad3)
|
val outStateBad3 = UniversalContract.State(listOf(DUMMY_NOTARY), transferBad3)
|
||||||
|
|
||||||
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
val inState = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `issue - signature`() {
|
fun `issue - signature`() {
|
||||||
|
@ -122,12 +122,12 @@ class IRS {
|
|||||||
|
|
||||||
val paymentFirst = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
val paymentFirst = arrange { highStreetBank.owes(acmeCorp, 250.K, EUR) }
|
||||||
|
|
||||||
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial)
|
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
|
||||||
|
|
||||||
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterFixingFirst)
|
val stateAfterFixingFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterFixingFirst)
|
||||||
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractAfterExecutionFirst)
|
val stateAfterExecutionFirst = UniversalContract.State(listOf(DUMMY_NOTARY), contractAfterExecutionFirst)
|
||||||
|
|
||||||
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), paymentFirst)
|
val statePaymentFirst = UniversalContract.State(listOf(DUMMY_NOTARY), paymentFirst)
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -36,7 +36,7 @@ class RollOutTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
val stateStart = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
|
||||||
|
|
||||||
val contractStep1a = arrange {
|
val contractStep1a = arrange {
|
||||||
rollOut("2016-10-03".ld, "2017-09-01".ld, Frequency.Monthly) {
|
rollOut("2016-10-03".ld, "2017-09-01".ld, Frequency.Monthly) {
|
||||||
@ -55,8 +55,8 @@ class RollOutTests {
|
|||||||
highStreetBank.owes(acmeCorp, 10.K, USD)
|
highStreetBank.owes(acmeCorp, 10.K, USD)
|
||||||
}
|
}
|
||||||
|
|
||||||
val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1a)
|
val stateStep1a = UniversalContract.State(listOf(DUMMY_NOTARY), contractStep1a)
|
||||||
val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractStep1b)
|
val stateStep1b = UniversalContract.State(listOf(DUMMY_NOTARY), contractStep1b)
|
||||||
|
|
||||||
val contract_transfer1 = arrange {
|
val contract_transfer1 = arrange {
|
||||||
highStreetBank.owes(acmeCorp, 10.K, USD)
|
highStreetBank.owes(acmeCorp, 10.K, USD)
|
||||||
|
@ -54,7 +54,7 @@ class Swaption {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractInitial)
|
val stateInitial = UniversalContract.State(listOf(DUMMY_NOTARY), contractInitial)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun issue() {
|
fun issue() {
|
||||||
|
@ -33,12 +33,12 @@ class ZeroCouponBond {
|
|||||||
val transfer = arrange { highStreetBank.owes(acmeCorp, 100.K, GBP) }
|
val transfer = arrange { highStreetBank.owes(acmeCorp, 100.K, GBP) }
|
||||||
val transferWrong = arrange { highStreetBank.owes(acmeCorp, 80.K, GBP) }
|
val transferWrong = arrange { highStreetBank.owes(acmeCorp, 80.K, GBP) }
|
||||||
|
|
||||||
val inState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contract)
|
val inState = UniversalContract.State(listOf(DUMMY_NOTARY), contract)
|
||||||
|
|
||||||
val outState = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transfer)
|
val outState = UniversalContract.State(listOf(DUMMY_NOTARY), transfer)
|
||||||
val outStateWrong = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), transferWrong)
|
val outStateWrong = UniversalContract.State(listOf(DUMMY_NOTARY), transferWrong)
|
||||||
|
|
||||||
val outStateMove = UniversalContract.State(listOf(DUMMY_NOTARY.owningKey), contractMove)
|
val outStateMove = UniversalContract.State(listOf(DUMMY_NOTARY), contractMove)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun basic() {
|
fun basic() {
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.contracts.isolated
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ val ANOTHER_DUMMY_PROGRAM_ID = AnotherDummyContract()
|
|||||||
class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor {
|
class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor {
|
||||||
data class State(val magicNumber: Int = 0) : ContractState {
|
data class State(val magicNumber: Int = 0) : ContractState {
|
||||||
override val contract = ANOTHER_DUMMY_PROGRAM_ID
|
override val contract = ANOTHER_DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = emptyList()
|
get() = emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.contracts;
|
package net.corda.contracts;
|
||||||
|
|
||||||
import net.corda.core.contracts.*;
|
import net.corda.core.contracts.*;
|
||||||
|
import net.corda.core.identity.AbstractParty;
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.time.*;
|
import java.time.*;
|
||||||
@ -12,7 +13,7 @@ import java.util.*;
|
|||||||
* ultimately either language can be used against a common test framework (and therefore can be used for real).
|
* ultimately either language can be used against a common test framework (and therefore can be used for real).
|
||||||
*/
|
*/
|
||||||
public interface ICommercialPaperState extends ContractState {
|
public interface ICommercialPaperState extends ContractState {
|
||||||
ICommercialPaperState withOwner(PublicKey newOwner);
|
ICommercialPaperState withOwner(AbstractParty newOwner);
|
||||||
|
|
||||||
ICommercialPaperState withFaceValue(Amount<Issued<Currency>> newFaceValue);
|
ICommercialPaperState withFaceValue(Amount<Issued<Currency>> newFaceValue);
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ import net.corda.core.contracts.Contract;
|
|||||||
import net.corda.core.contracts.TransactionForContract.*;
|
import net.corda.core.contracts.TransactionForContract.*;
|
||||||
import net.corda.core.contracts.clauses.*;
|
import net.corda.core.contracts.clauses.*;
|
||||||
import net.corda.core.crypto.*;
|
import net.corda.core.crypto.*;
|
||||||
|
import net.corda.core.identity.AbstractParty;
|
||||||
|
import net.corda.core.identity.AbstractParty;
|
||||||
|
import net.corda.core.identity.AnonymousParty;
|
||||||
import net.corda.core.identity.Party;
|
import net.corda.core.identity.Party;
|
||||||
import net.corda.core.node.services.*;
|
import net.corda.core.node.services.*;
|
||||||
import net.corda.core.transactions.*;
|
import net.corda.core.transactions.*;
|
||||||
@ -34,14 +37,14 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static class State implements OwnableState, ICommercialPaperState {
|
public static class State implements OwnableState, ICommercialPaperState {
|
||||||
private PartyAndReference issuance;
|
private PartyAndReference issuance;
|
||||||
private PublicKey owner;
|
private AbstractParty owner;
|
||||||
private Amount<Issued<Currency>> faceValue;
|
private Amount<Issued<Currency>> faceValue;
|
||||||
private Instant maturityDate;
|
private Instant maturityDate;
|
||||||
|
|
||||||
public State() {
|
public State() {
|
||||||
} // For serialization
|
} // For serialization
|
||||||
|
|
||||||
public State(PartyAndReference issuance, PublicKey owner, Amount<Issued<Currency>> faceValue,
|
public State(PartyAndReference issuance, AbstractParty owner, Amount<Issued<Currency>> faceValue,
|
||||||
Instant maturityDate) {
|
Instant maturityDate) {
|
||||||
this.issuance = issuance;
|
this.issuance = issuance;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@ -53,13 +56,13 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
return new State(this.issuance, this.owner, this.faceValue, this.maturityDate);
|
return new State(this.issuance, this.owner, this.faceValue, this.maturityDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommercialPaperState withOwner(PublicKey newOwner) {
|
public ICommercialPaperState withOwner(AbstractParty newOwner) {
|
||||||
return new State(this.issuance, newOwner, this.faceValue, this.maturityDate);
|
return new State(this.issuance, newOwner, this.faceValue, this.maturityDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Pair<CommandData, OwnableState> withNewOwner(@NotNull PublicKey newOwner) {
|
public Pair<CommandData, OwnableState> withNewOwner(@NotNull AbstractParty newOwner) {
|
||||||
return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
|
return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +79,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public PublicKey getOwner() {
|
public AbstractParty getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,12 +120,12 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public State withoutOwner() {
|
public State withoutOwner() {
|
||||||
return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate);
|
return new State(issuance, new AnonymousParty(NullPublicKey.INSTANCE), faceValue, maturityDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<PublicKey> getParticipants() {
|
public List<AbstractParty> getParticipants() {
|
||||||
return ImmutableList.of(this.owner);
|
return ImmutableList.of(this.owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,12 +165,12 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
@NotNull List<? extends State> inputs,
|
@NotNull List<? extends State> inputs,
|
||||||
@NotNull List<? extends State> outputs,
|
@NotNull List<? extends State> outputs,
|
||||||
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
|
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
|
||||||
@NotNull State groupingKey) {
|
State groupingKey) {
|
||||||
AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
|
AuthenticatedObject<Commands.Move> cmd = requireSingleCommand(tx.getCommands(), Commands.Move.class);
|
||||||
// There should be only a single input due to aggregation above
|
// There should be only a single input due to aggregation above
|
||||||
State input = single(inputs);
|
State input = single(inputs);
|
||||||
|
|
||||||
if (!cmd.getSigners().contains(input.getOwner()))
|
if (!cmd.getSigners().contains(input.getOwner().getOwningKey()))
|
||||||
throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP");
|
throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP");
|
||||||
|
|
||||||
// Check the output CP state is the same as the input state, ignoring the owner field.
|
// Check the output CP state is the same as the input state, ignoring the owner field.
|
||||||
@ -194,13 +197,13 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
@NotNull List<? extends State> inputs,
|
@NotNull List<? extends State> inputs,
|
||||||
@NotNull List<? extends State> outputs,
|
@NotNull List<? extends State> outputs,
|
||||||
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
|
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
|
||||||
@NotNull State groupingKey) {
|
State groupingKey) {
|
||||||
AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class);
|
AuthenticatedObject<Commands.Redeem> cmd = requireSingleCommand(tx.getCommands(), Commands.Redeem.class);
|
||||||
|
|
||||||
// There should be only a single input due to aggregation above
|
// There should be only a single input due to aggregation above
|
||||||
State input = single(inputs);
|
State input = single(inputs);
|
||||||
|
|
||||||
if (!cmd.getSigners().contains(input.getOwner()))
|
if (!cmd.getSigners().contains(input.getOwner().getOwningKey()))
|
||||||
throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP");
|
throw new IllegalStateException("Failed requirement: the transaction is signed by the owner of the CP");
|
||||||
|
|
||||||
Timestamp timestamp = tx.getTimestamp();
|
Timestamp timestamp = tx.getTimestamp();
|
||||||
@ -237,7 +240,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
@NotNull List<? extends State> inputs,
|
@NotNull List<? extends State> inputs,
|
||||||
@NotNull List<? extends State> outputs,
|
@NotNull List<? extends State> outputs,
|
||||||
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
|
@NotNull List<? extends AuthenticatedObject<? extends Commands>> commands,
|
||||||
@NotNull State groupingKey) {
|
State groupingKey) {
|
||||||
AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class);
|
AuthenticatedObject<Commands.Issue> cmd = requireSingleCommand(tx.getCommands(), Commands.Issue.class);
|
||||||
State output = single(outputs);
|
State output = single(outputs);
|
||||||
Timestamp timestampCommand = tx.getTimestamp();
|
Timestamp timestampCommand = tx.getTimestamp();
|
||||||
@ -304,7 +307,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) {
|
public TransactionBuilder generateIssue(@NotNull PartyAndReference issuance, @NotNull Amount<Issued<Currency>> faceValue, @Nullable Instant maturityDate, @NotNull Party notary, Integer encumbrance) {
|
||||||
State state = new State(issuance, issuance.getParty().getOwningKey(), faceValue, maturityDate);
|
State state = new State(issuance, issuance.getParty(), faceValue, maturityDate);
|
||||||
TransactionState output = new TransactionState<>(state, notary, encumbrance);
|
TransactionState output = new TransactionState<>(state, notary, encumbrance);
|
||||||
return new TransactionType.General.Builder(notary).withItems(output, new Command(new Commands.Issue(), issuance.getParty().getOwningKey()));
|
return new TransactionType.General.Builder(notary).withItems(output, new Command(new Commands.Issue(), issuance.getParty().getOwningKey()));
|
||||||
}
|
}
|
||||||
@ -317,12 +320,12 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, VaultService vault) throws InsufficientBalanceException {
|
public void generateRedeem(TransactionBuilder tx, StateAndRef<State> paper, VaultService vault) throws InsufficientBalanceException {
|
||||||
vault.generateSpend(tx, StructuresKt.withoutIssuer(paper.getState().getData().getFaceValue()), paper.getState().getData().getOwner(), null);
|
vault.generateSpend(tx, StructuresKt.withoutIssuer(paper.getState().getData().getFaceValue()), paper.getState().getData().getOwner(), null);
|
||||||
tx.addInputState(paper);
|
tx.addInputState(paper);
|
||||||
tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner()));
|
tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner().getOwningKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKey newOwner) {
|
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, AbstractParty newOwner) {
|
||||||
tx.addInputState(paper);
|
tx.addInputState(paper);
|
||||||
tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance()));
|
tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance()));
|
||||||
tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner()));
|
tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner().getOwningKey()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import net.corda.core.contracts.clauses.GroupClauseVerifier
|
|||||||
import net.corda.core.contracts.clauses.verifyClause
|
import net.corda.core.contracts.clauses.verifyClause
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toBase58String
|
import net.corda.core.crypto.toBase58String
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.random63BitValue
|
import net.corda.core.random63BitValue
|
||||||
@ -19,7 +20,6 @@ import net.corda.core.schemas.QueryableState
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.Emoji
|
import net.corda.core.utilities.Emoji
|
||||||
import net.corda.schemas.CommercialPaperSchemaV1
|
import net.corda.schemas.CommercialPaperSchemaV1
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -61,22 +61,22 @@ class CommercialPaper : Contract {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val issuance: PartyAndReference,
|
val issuance: PartyAndReference,
|
||||||
override val owner: PublicKey,
|
override val owner: AbstractParty,
|
||||||
val faceValue: Amount<Issued<Currency>>,
|
val faceValue: Amount<Issued<Currency>>,
|
||||||
val maturityDate: Instant
|
val maturityDate: Instant
|
||||||
) : OwnableState, QueryableState, ICommercialPaperState {
|
) : OwnableState, QueryableState, ICommercialPaperState {
|
||||||
override val contract = CP_PROGRAM_ID
|
override val contract = CP_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
val token: Issued<Terms>
|
val token: Issued<Terms>
|
||||||
get() = Issued(issuance, Terms(faceValue.token, maturityDate))
|
get() = Issued(issuance, Terms(faceValue.token, maturityDate))
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
|
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
|
||||||
|
|
||||||
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
||||||
override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner)
|
override fun withOwner(newOwner: AbstractParty): ICommercialPaperState = copy(owner = newOwner)
|
||||||
|
|
||||||
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
||||||
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
|
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
|
||||||
@ -91,7 +91,7 @@ class CommercialPaper : Contract {
|
|||||||
is CommercialPaperSchemaV1 -> CommercialPaperSchemaV1.PersistentCommercialPaperState(
|
is CommercialPaperSchemaV1 -> CommercialPaperSchemaV1.PersistentCommercialPaperState(
|
||||||
issuanceParty = this.issuance.party.owningKey.toBase58String(),
|
issuanceParty = this.issuance.party.owningKey.toBase58String(),
|
||||||
issuanceRef = this.issuance.reference.bytes,
|
issuanceRef = this.issuance.reference.bytes,
|
||||||
owner = this.owner.toBase58String(),
|
owner = this.owner.owningKey.toBase58String(),
|
||||||
maturity = this.maturityDate,
|
maturity = this.maturityDate,
|
||||||
faceValue = this.faceValue.quantity,
|
faceValue = this.faceValue.quantity,
|
||||||
currency = this.faceValue.token.product.currencyCode,
|
currency = this.faceValue.token.product.currencyCode,
|
||||||
@ -146,7 +146,7 @@ class CommercialPaper : Contract {
|
|||||||
val command = commands.requireSingleCommand<Commands.Move>()
|
val command = commands.requireSingleCommand<Commands.Move>()
|
||||||
val input = inputs.single()
|
val input = inputs.single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"the transaction is signed by the owner of the CP" using (input.owner in command.signers)
|
"the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
|
||||||
"the state is propagated" using (outputs.size == 1)
|
"the state is propagated" using (outputs.size == 1)
|
||||||
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to
|
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to
|
||||||
// the input ignoring the owner field due to the grouping.
|
// the input ignoring the owner field due to the grouping.
|
||||||
@ -175,7 +175,7 @@ class CommercialPaper : Contract {
|
|||||||
"the paper must have matured" using (time >= input.maturityDate)
|
"the paper must have matured" using (time >= input.maturityDate)
|
||||||
"the received amount equals the face value" using (received == input.faceValue)
|
"the received amount equals the face value" using (received == input.faceValue)
|
||||||
"the paper must be destroyed" using outputs.isEmpty()
|
"the paper must be destroyed" using outputs.isEmpty()
|
||||||
"the transaction is signed by the owner of the CP" using (input.owner in command.signers)
|
"the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
|
||||||
}
|
}
|
||||||
|
|
||||||
return setOf(command.value)
|
return setOf(command.value)
|
||||||
@ -196,17 +196,17 @@ class CommercialPaper : Contract {
|
|||||||
* at the moment: this restriction is not fundamental and may be lifted later.
|
* at the moment: this restriction is not fundamental and may be lifted later.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant, notary: Party): TransactionBuilder {
|
fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant, notary: Party): TransactionBuilder {
|
||||||
val state = TransactionState(State(issuance, issuance.party.owningKey, faceValue, maturityDate), notary)
|
val state = TransactionState(State(issuance, issuance.party, faceValue, maturityDate), notary)
|
||||||
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
|
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
||||||
*/
|
*/
|
||||||
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
|
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: AbstractParty) {
|
||||||
tx.addInputState(paper)
|
tx.addInputState(paper)
|
||||||
tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary))
|
tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary))
|
||||||
tx.addCommand(Commands.Move(), paper.state.data.owner)
|
tx.addCommand(Commands.Move(), paper.state.data.owner.owningKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,12 +223,12 @@ class CommercialPaper : Contract {
|
|||||||
val amount = paper.state.data.faceValue.let { amount -> Amount(amount.quantity, amount.token.product) }
|
val amount = paper.state.data.faceValue.let { amount -> Amount(amount.quantity, amount.token.product) }
|
||||||
vault.generateSpend(tx, amount, paper.state.data.owner)
|
vault.generateSpend(tx, amount, paper.state.data.owner)
|
||||||
tx.addInputState(paper)
|
tx.addInputState(paper)
|
||||||
tx.addCommand(CommercialPaper.Commands.Redeem(), paper.state.data.owner)
|
tx.addCommand(CommercialPaper.Commands.Redeem(), paper.state.data.owner.owningKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun CommercialPaper.State.`owned by`(owner: PublicKey) = copy(owner = owner)
|
infix fun CommercialPaper.State.`owned by`(owner: AbstractParty) = copy(owner = owner)
|
||||||
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
|
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
|
||||||
infix fun ICommercialPaperState.`owned by`(newOwner: PublicKey) = withOwner(newOwner)
|
infix fun ICommercialPaperState.`owned by`(newOwner: AbstractParty) = withOwner(newOwner)
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@ package net.corda.contracts
|
|||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.contracts.asset.sumCashBy
|
import net.corda.contracts.asset.sumCashBy
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.NullPublicKey
|
import net.corda.core.crypto.NULL_PARTY
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.Emoji
|
import net.corda.core.utilities.Emoji
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -27,19 +27,19 @@ class CommercialPaperLegacy : Contract {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val issuance: PartyAndReference,
|
val issuance: PartyAndReference,
|
||||||
override val owner: PublicKey,
|
override val owner: AbstractParty,
|
||||||
val faceValue: Amount<Issued<Currency>>,
|
val faceValue: Amount<Issued<Currency>>,
|
||||||
val maturityDate: Instant
|
val maturityDate: Instant
|
||||||
) : OwnableState, ICommercialPaperState {
|
) : OwnableState, ICommercialPaperState {
|
||||||
override val contract = CP_LEGACY_PROGRAM_ID
|
override val contract = CP_LEGACY_PROGRAM_ID
|
||||||
override val participants = listOf(owner)
|
override val participants = listOf(owner)
|
||||||
|
|
||||||
fun withoutOwner() = copy(owner = NullPublicKey)
|
fun withoutOwner() = copy(owner = NULL_PARTY)
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
|
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
|
||||||
|
|
||||||
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
||||||
override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner)
|
override fun withOwner(newOwner: AbstractParty): ICommercialPaperState = copy(owner = newOwner)
|
||||||
|
|
||||||
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
||||||
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
|
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
|
||||||
@ -70,7 +70,7 @@ class CommercialPaperLegacy : Contract {
|
|||||||
is Commands.Move -> {
|
is Commands.Move -> {
|
||||||
val input = inputs.single()
|
val input = inputs.single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"the transaction is signed by the owner of the CP" using (input.owner in command.signers)
|
"the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
|
||||||
"the state is propagated" using (outputs.size == 1)
|
"the state is propagated" using (outputs.size == 1)
|
||||||
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to
|
// Don't need to check anything else, as if outputs.size == 1 then the output is equal to
|
||||||
// the input ignoring the owner field due to the grouping.
|
// the input ignoring the owner field due to the grouping.
|
||||||
@ -86,7 +86,7 @@ class CommercialPaperLegacy : Contract {
|
|||||||
"the paper must have matured" using (time >= input.maturityDate)
|
"the paper must have matured" using (time >= input.maturityDate)
|
||||||
"the received amount equals the face value" using (received == input.faceValue)
|
"the received amount equals the face value" using (received == input.faceValue)
|
||||||
"the paper must be destroyed" using outputs.isEmpty()
|
"the paper must be destroyed" using outputs.isEmpty()
|
||||||
"the transaction is signed by the owner of the CP" using (input.owner in command.signers)
|
"the transaction is signed by the owner of the CP" using (input.owner.owningKey in command.signers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,14 +114,14 @@ class CommercialPaperLegacy : Contract {
|
|||||||
|
|
||||||
fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant,
|
fun generateIssue(issuance: PartyAndReference, faceValue: Amount<Issued<Currency>>, maturityDate: Instant,
|
||||||
notary: Party): TransactionBuilder {
|
notary: Party): TransactionBuilder {
|
||||||
val state = State(issuance, issuance.party.owningKey, faceValue, maturityDate)
|
val state = State(issuance, issuance.party, faceValue, maturityDate)
|
||||||
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
|
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
|
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: AbstractParty) {
|
||||||
tx.addInputState(paper)
|
tx.addInputState(paper)
|
||||||
tx.addOutputState(paper.state.data.withOwner(newOwner))
|
tx.addOutputState(paper.state.data.withOwner(newOwner))
|
||||||
tx.addCommand(Command(Commands.Move(), paper.state.data.owner))
|
tx.addCommand(Command(Commands.Move(), paper.state.data.owner.owningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(InsufficientBalanceException::class)
|
@Throws(InsufficientBalanceException::class)
|
||||||
@ -130,6 +130,6 @@ class CommercialPaperLegacy : Contract {
|
|||||||
// Add the cash movement using the states in our vault.
|
// Add the cash movement using the states in our vault.
|
||||||
vault.generateSpend(tx, paper.state.data.faceValue.withoutIssuer(), paper.state.data.owner)
|
vault.generateSpend(tx, paper.state.data.faceValue.withoutIssuer(), paper.state.data.owner)
|
||||||
tx.addInputState(paper)
|
tx.addInputState(paper)
|
||||||
tx.addCommand(Command(Commands.Redeem(), paper.state.data.owner))
|
tx.addCommand(Command(Commands.Redeem(), paper.state.data.owner.owningKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import net.corda.core.utilities.Emoji
|
|||||||
import net.corda.schemas.CashSchemaV1
|
import net.corda.schemas.CashSchemaV1
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -87,27 +86,27 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
|
|||||||
override val amount: Amount<Issued<Currency>>,
|
override val amount: Amount<Issued<Currency>>,
|
||||||
|
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount. */
|
/** There must be a MoveCommand signed by this key to claim the amount. */
|
||||||
override val owner: PublicKey
|
override val owner: AbstractParty
|
||||||
) : FungibleAsset<Currency>, QueryableState {
|
) : FungibleAsset<Currency>, QueryableState {
|
||||||
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKey)
|
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: AbstractParty)
|
||||||
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
||||||
|
|
||||||
override val exitKeys = setOf(owner, amount.token.issuer.party.owningKey)
|
override val exitKeys = setOf(owner.owningKey, amount.token.issuer.party.owningKey)
|
||||||
override val contract = CASH_PROGRAM_ID
|
override val contract = CASH_PROGRAM_ID
|
||||||
override val participants = listOf(owner)
|
override val participants = listOf(owner)
|
||||||
|
|
||||||
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKey): FungibleAsset<Currency>
|
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: AbstractParty): FungibleAsset<Currency>
|
||||||
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
|
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
|
||||||
|
|
||||||
override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)"
|
override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)"
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
|
|
||||||
/** Object Relational Mapping support. */
|
/** Object Relational Mapping support. */
|
||||||
override fun generateMappedObject(schema: MappedSchema): PersistentState {
|
override fun generateMappedObject(schema: MappedSchema): PersistentState {
|
||||||
return when (schema) {
|
return when (schema) {
|
||||||
is CashSchemaV1 -> CashSchemaV1.PersistentCashState(
|
is CashSchemaV1 -> CashSchemaV1.PersistentCashState(
|
||||||
owner = this.owner.toBase58String(),
|
owner = this.owner.owningKey.toBase58String(),
|
||||||
pennies = this.amount.quantity,
|
pennies = this.amount.quantity,
|
||||||
currency = this.amount.token.product.currencyCode,
|
currency = this.amount.token.product.currencyCode,
|
||||||
issuerParty = this.amount.token.issuer.party.owningKey.toBase58String(),
|
issuerParty = this.amount.token.issuer.party.owningKey.toBase58String(),
|
||||||
@ -148,16 +147,16 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
|
|||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKey, notary: Party)
|
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: AbstractParty, notary: Party)
|
||||||
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKey, notary: Party)
|
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: AbstractParty, notary: Party)
|
||||||
= generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand())
|
= generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand())
|
||||||
|
|
||||||
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
|
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: AbstractParty)
|
||||||
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
||||||
|
|
||||||
override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount)
|
override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount)
|
||||||
@ -175,7 +174,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
|
|||||||
* if there are none, or if any of the cash states cannot be added together (i.e. are
|
* if there are none, or if any of the cash states cannot be added together (i.e. are
|
||||||
* different currencies or issuers).
|
* different currencies or issuers).
|
||||||
*/
|
*/
|
||||||
fun Iterable<ContractState>.sumCashBy(owner: PublicKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
|
fun Iterable<ContractState>.sumCashBy(owner: AbstractParty): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
|
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
|
||||||
@ -191,12 +190,12 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
|
|||||||
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
|
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Cash.State.ownedBy(owner: PublicKey) = copy(owner = owner)
|
fun Cash.State.ownedBy(owner: AbstractParty) = copy(owner = owner)
|
||||||
fun Cash.State.issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party.toAnonymous()))))
|
fun Cash.State.issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party))))
|
||||||
fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit)))
|
fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit)))
|
||||||
fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))
|
fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))
|
||||||
|
|
||||||
infix fun Cash.State.`owned by`(owner: PublicKey) = ownedBy(owner)
|
infix fun Cash.State.`owned by`(owner: AbstractParty) = ownedBy(owner)
|
||||||
infix fun Cash.State.`issued by`(party: AbstractParty) = issuedBy(party)
|
infix fun Cash.State.`issued by`(party: AbstractParty) = issuedBy(party)
|
||||||
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
|
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
|
||||||
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)
|
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)
|
||||||
@ -208,6 +207,6 @@ val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
|
|||||||
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
|
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
|
||||||
val DUMMY_CASH_ISSUER by lazy { Party(X500Name("CN=Snake Oil Issuer,O=R3,OU=corda,L=London,C=UK"), DUMMY_CASH_ISSUER_KEY.public).ref(1) }
|
val DUMMY_CASH_ISSUER by lazy { Party(X500Name("CN=Snake Oil Issuer,O=R3,OU=corda,L=London,C=UK"), DUMMY_CASH_ISSUER_KEY.public).ref(1) }
|
||||||
/** An extension property that lets you write 100.DOLLARS.CASH */
|
/** An extension property that lets you write 100.DOLLARS.CASH */
|
||||||
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey)
|
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NULL_PARTY)
|
||||||
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */
|
/** An extension property that lets you get a cash state from an issued token, under the [NULL_PARTY] */
|
||||||
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKey)
|
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NULL_PARTY)
|
||||||
|
@ -9,10 +9,10 @@ import net.corda.core.contracts.clauses.GroupClauseVerifier
|
|||||||
import net.corda.core.contracts.clauses.verifyClause
|
import net.corda.core.contracts.clauses.verifyClause
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.newSecureRandom
|
import net.corda.core.crypto.newSecureRandom
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -94,21 +94,21 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
|
|||||||
override val amount: Amount<Issued<Commodity>>,
|
override val amount: Amount<Issued<Commodity>>,
|
||||||
|
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||||
override val owner: PublicKey
|
override val owner: AbstractParty
|
||||||
) : FungibleAsset<Commodity> {
|
) : FungibleAsset<Commodity> {
|
||||||
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKey)
|
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: AbstractParty)
|
||||||
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
||||||
|
|
||||||
override val contract = COMMODITY_PROGRAM_ID
|
override val contract = COMMODITY_PROGRAM_ID
|
||||||
override val exitKeys = Collections.singleton(owner)
|
override val exitKeys = Collections.singleton(owner.owningKey)
|
||||||
override val participants = listOf(owner)
|
override val participants = listOf(owner)
|
||||||
|
|
||||||
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKey): FungibleAsset<Commodity>
|
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: AbstractParty): FungibleAsset<Commodity>
|
||||||
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
|
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
|
||||||
|
|
||||||
override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)"
|
override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)"
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just for grouping
|
// Just for grouping
|
||||||
@ -145,17 +145,17 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
|
|||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKey, notary: Party)
|
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: AbstractParty, notary: Party)
|
||||||
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKey, notary: Party)
|
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: AbstractParty, notary: Party)
|
||||||
= generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand())
|
= generateIssue(tx, TransactionState(State(amount, owner), notary), generateIssueCommand())
|
||||||
|
|
||||||
|
|
||||||
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKey)
|
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: AbstractParty)
|
||||||
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
||||||
|
|
||||||
override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount)
|
override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount)
|
||||||
|
@ -5,7 +5,9 @@ import net.corda.contracts.asset.Obligation.Lifecycle.NORMAL
|
|||||||
import net.corda.contracts.clause.*
|
import net.corda.contracts.clause.*
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.*
|
import net.corda.core.contracts.clauses.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.NULL_PARTY
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.entropyToKeyPair
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
@ -22,6 +24,36 @@ import java.security.PublicKey
|
|||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.Collection
|
||||||
|
import kotlin.collections.Iterable
|
||||||
|
import kotlin.collections.List
|
||||||
|
import kotlin.collections.Map
|
||||||
|
import kotlin.collections.Set
|
||||||
|
import kotlin.collections.all
|
||||||
|
import kotlin.collections.asIterable
|
||||||
|
import kotlin.collections.component1
|
||||||
|
import kotlin.collections.component2
|
||||||
|
import kotlin.collections.contains
|
||||||
|
import kotlin.collections.distinct
|
||||||
|
import kotlin.collections.emptySet
|
||||||
|
import kotlin.collections.filter
|
||||||
|
import kotlin.collections.filterIsInstance
|
||||||
|
import kotlin.collections.first
|
||||||
|
import kotlin.collections.firstOrNull
|
||||||
|
import kotlin.collections.forEach
|
||||||
|
import kotlin.collections.groupBy
|
||||||
|
import kotlin.collections.isNotEmpty
|
||||||
|
import kotlin.collections.iterator
|
||||||
|
import kotlin.collections.listOf
|
||||||
|
import kotlin.collections.map
|
||||||
|
import kotlin.collections.none
|
||||||
|
import kotlin.collections.reduce
|
||||||
|
import kotlin.collections.set
|
||||||
|
import kotlin.collections.setOf
|
||||||
|
import kotlin.collections.single
|
||||||
|
import kotlin.collections.toSet
|
||||||
|
import kotlin.collections.union
|
||||||
|
import kotlin.collections.withIndex
|
||||||
|
|
||||||
// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode.
|
// Just a fake program identifier for now. In a real system it could be, for instance, the hash of the program bytecode.
|
||||||
val OBLIGATION_PROGRAM_ID = Obligation<Currency>()
|
val OBLIGATION_PROGRAM_ID = Obligation<Currency>()
|
||||||
@ -274,27 +306,20 @@ class Obligation<P : Any> : Contract {
|
|||||||
data class State<P : Any>(
|
data class State<P : Any>(
|
||||||
var lifecycle: Lifecycle = Lifecycle.NORMAL,
|
var lifecycle: Lifecycle = Lifecycle.NORMAL,
|
||||||
/** Where the debt originates from (obligor) */
|
/** Where the debt originates from (obligor) */
|
||||||
val obligor: AnonymousParty,
|
val obligor: AbstractParty,
|
||||||
val template: Terms<P>,
|
val template: Terms<P>,
|
||||||
val quantity: Long,
|
val quantity: Long,
|
||||||
/** The public key of the entity the contract pays to */
|
/** The public key of the entity the contract pays to */
|
||||||
val beneficiary: PublicKey
|
val beneficiary: AbstractParty
|
||||||
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
|
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
|
||||||
constructor(lifecycle: Lifecycle = Lifecycle.NORMAL,
|
|
||||||
obligor: Party,
|
|
||||||
template: Terms<P>,
|
|
||||||
quantity: Long,
|
|
||||||
beneficiary: PublicKey)
|
|
||||||
: this(lifecycle, obligor.toAnonymous(), template, quantity, beneficiary)
|
|
||||||
|
|
||||||
override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template))
|
override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template))
|
||||||
override val contract = OBLIGATION_PROGRAM_ID
|
override val contract = OBLIGATION_PROGRAM_ID
|
||||||
override val exitKeys: Collection<PublicKey> = setOf(beneficiary)
|
override val exitKeys: Collection<PublicKey> = setOf(beneficiary.owningKey)
|
||||||
val dueBefore: Instant = template.dueBefore
|
val dueBefore: Instant = template.dueBefore
|
||||||
override val participants: List<PublicKey> = listOf(obligor.owningKey, beneficiary)
|
override val participants: List<AbstractParty> = listOf(obligor, beneficiary)
|
||||||
override val owner: PublicKey = beneficiary
|
override val owner: AbstractParty = beneficiary
|
||||||
|
|
||||||
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKey): State<P>
|
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: AbstractParty): State<P>
|
||||||
= copy(quantity = newAmount.quantity, beneficiary = newOwner)
|
= copy(quantity = newAmount.quantity, beneficiary = newOwner)
|
||||||
|
|
||||||
override fun toString() = when (lifecycle) {
|
override fun toString() = when (lifecycle) {
|
||||||
@ -305,7 +330,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
override val bilateralNetState: BilateralNetState<P>
|
override val bilateralNetState: BilateralNetState<P>
|
||||||
get() {
|
get() {
|
||||||
check(lifecycle == Lifecycle.NORMAL)
|
check(lifecycle == Lifecycle.NORMAL)
|
||||||
return BilateralNetState(setOf(obligor.owningKey, beneficiary), template)
|
return BilateralNetState(setOf(obligor, beneficiary), template)
|
||||||
}
|
}
|
||||||
override val multilateralNetState: MultilateralNetState<P>
|
override val multilateralNetState: MultilateralNetState<P>
|
||||||
get() {
|
get() {
|
||||||
@ -327,7 +352,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(beneficiary = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Move(), copy(beneficiary = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just for grouping
|
// Just for grouping
|
||||||
@ -418,7 +443,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val owningPubKeys = inputs.filter { it is State<P> }.map { (it as State<P>).beneficiary }.toSet()
|
val owningPubKeys = inputs.filter { it is State<P> }.map { (it as State<P>).beneficiary.owningKey }.toSet()
|
||||||
val keysThatSigned = setLifecycleCommand.signers.toSet()
|
val keysThatSigned = setLifecycleCommand.signers.toSet()
|
||||||
requireThat {
|
requireThat {
|
||||||
"the owning keys are a subset of the signing keys" using keysThatSigned.containsAll(owningPubKeys)
|
"the owning keys are a subset of the signing keys" using keysThatSigned.containsAll(owningPubKeys)
|
||||||
@ -433,7 +458,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
* and same parties involved).
|
* and same parties involved).
|
||||||
*/
|
*/
|
||||||
fun generateCloseOutNetting(tx: TransactionBuilder,
|
fun generateCloseOutNetting(tx: TransactionBuilder,
|
||||||
signer: PublicKey,
|
signer: AbstractParty,
|
||||||
vararg states: State<P>) {
|
vararg states: State<P>) {
|
||||||
val netState = states.firstOrNull()?.bilateralNetState
|
val netState = states.firstOrNull()?.bilateralNetState
|
||||||
|
|
||||||
@ -447,7 +472,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
val out = states.reduce(State<P>::net)
|
val out = states.reduce(State<P>::net)
|
||||||
if (out.quantity > 0L)
|
if (out.quantity > 0L)
|
||||||
tx.addOutputState(out)
|
tx.addOutputState(out)
|
||||||
tx.addCommand(Commands.Net(NetType.PAYMENT), signer)
|
tx.addCommand(Commands.Net(NetType.PAYMENT), signer.owningKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -475,9 +500,9 @@ class Obligation<P : Any> : Contract {
|
|||||||
obligor: AbstractParty,
|
obligor: AbstractParty,
|
||||||
issuanceDef: Terms<P>,
|
issuanceDef: Terms<P>,
|
||||||
pennies: Long,
|
pennies: Long,
|
||||||
beneficiary: PublicKey,
|
beneficiary: AbstractParty,
|
||||||
notary: Party)
|
notary: Party)
|
||||||
= OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor.toAnonymous(), issuanceDef, pennies, beneficiary), notary), Commands.Issue())
|
= OnLedgerAsset.generateIssue(tx, TransactionState(State(Lifecycle.NORMAL, obligor, issuanceDef, pennies, beneficiary), notary), Commands.Issue())
|
||||||
|
|
||||||
fun generatePaymentNetting(tx: TransactionBuilder,
|
fun generatePaymentNetting(tx: TransactionBuilder,
|
||||||
issued: Issued<Obligation.Terms<P>>,
|
issued: Issued<Obligation.Terms<P>>,
|
||||||
@ -487,8 +512,8 @@ class Obligation<P : Any> : Contract {
|
|||||||
"all states are in the normal lifecycle state " using (states.all { it.lifecycle == Lifecycle.NORMAL })
|
"all states are in the normal lifecycle state " using (states.all { it.lifecycle == Lifecycle.NORMAL })
|
||||||
}
|
}
|
||||||
val groups = states.groupBy { it.multilateralNetState }
|
val groups = states.groupBy { it.multilateralNetState }
|
||||||
val partyLookup = HashMap<PublicKey, AnonymousParty>()
|
val partyLookup = HashMap<PublicKey, AbstractParty>()
|
||||||
val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).toSet()
|
val signers = states.map { it.beneficiary }.union(states.map { it.obligor }).toSet()
|
||||||
|
|
||||||
// Create a lookup table of the party that each public key represents.
|
// Create a lookup table of the party that each public key represents.
|
||||||
states.map { it.obligor }.forEach { partyLookup.put(it.owningKey, it) }
|
states.map { it.obligor }.forEach { partyLookup.put(it.owningKey, it) }
|
||||||
@ -502,12 +527,12 @@ class Obligation<P : Any> : Contract {
|
|||||||
netBalances
|
netBalances
|
||||||
// Convert the balances into obligation state objects
|
// Convert the balances into obligation state objects
|
||||||
.map { entry ->
|
.map { entry ->
|
||||||
State(Lifecycle.NORMAL, partyLookup[entry.key.first]!!,
|
State(Lifecycle.NORMAL, entry.key.first,
|
||||||
netState.template, entry.value.quantity, entry.key.second)
|
netState.template, entry.value.quantity, entry.key.second)
|
||||||
}
|
}
|
||||||
// Add the new states to the TX
|
// Add the new states to the TX
|
||||||
.forEach { tx.addOutputState(it, notary) }
|
.forEach { tx.addOutputState(it, notary) }
|
||||||
tx.addCommand(Commands.Net(NetType.PAYMENT), signers.toList())
|
tx.addCommand(Commands.Net(NetType.PAYMENT), signers.map { it.owningKey })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -533,14 +558,14 @@ class Obligation<P : Any> : Contract {
|
|||||||
// Produce a new set of states
|
// Produce a new set of states
|
||||||
val groups = statesAndRefs.groupBy { it.state.data.amount.token }
|
val groups = statesAndRefs.groupBy { it.state.data.amount.token }
|
||||||
for ((_, stateAndRefs) in groups) {
|
for ((_, stateAndRefs) in groups) {
|
||||||
val partiesUsed = ArrayList<PublicKey>()
|
val partiesUsed = ArrayList<AbstractParty>()
|
||||||
stateAndRefs.forEach { stateAndRef ->
|
stateAndRefs.forEach { stateAndRef ->
|
||||||
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
|
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
|
||||||
tx.addInputState(stateAndRef)
|
tx.addInputState(stateAndRef)
|
||||||
tx.addOutputState(outState, notary)
|
tx.addOutputState(outState, notary)
|
||||||
partiesUsed.add(stateAndRef.state.data.beneficiary)
|
partiesUsed.add(stateAndRef.state.data.beneficiary)
|
||||||
}
|
}
|
||||||
tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.distinct())
|
tx.addCommand(Commands.SetLifecycle(lifecycle), partiesUsed.map { it.owningKey }.distinct())
|
||||||
}
|
}
|
||||||
tx.setTime(issuanceDef.dueBefore, issuanceDef.timeTolerance)
|
tx.setTime(issuanceDef.dueBefore, issuanceDef.timeTolerance)
|
||||||
}
|
}
|
||||||
@ -578,7 +603,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
val template: Terms<P> = issuanceDef.product
|
val template: Terms<P> = issuanceDef.product
|
||||||
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
|
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
|
||||||
var obligationRemaining: Amount<P> = obligationTotal
|
var obligationRemaining: Amount<P> = obligationTotal
|
||||||
val assetSigners = HashSet<PublicKey>()
|
val assetSigners = HashSet<AbstractParty>()
|
||||||
|
|
||||||
statesAndRefs.forEach { tx.addInputState(it) }
|
statesAndRefs.forEach { tx.addInputState(it) }
|
||||||
|
|
||||||
@ -611,7 +636,7 @@ class Obligation<P : Any> : Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the asset move command and obligation settle
|
// Add the asset move command and obligation settle
|
||||||
tx.addCommand(moveCommand, assetSigners.toList())
|
tx.addCommand(moveCommand, assetSigners.map { it.owningKey })
|
||||||
tx.addCommand(Commands.Settle(Amount((obligationTotal - obligationRemaining).quantity, issuanceDef)), obligationIssuer.owningKey)
|
tx.addCommand(Commands.Settle(Amount((obligationTotal - obligationRemaining).quantity, issuanceDef)), obligationIssuer.owningKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,11 +655,11 @@ class Obligation<P : Any> : Contract {
|
|||||||
*
|
*
|
||||||
* @return a map of obligor/beneficiary pairs to the balance due.
|
* @return a map of obligor/beneficiary pairs to the balance due.
|
||||||
*/
|
*/
|
||||||
fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>> {
|
fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<AbstractParty, AbstractParty>, Amount<Obligation.Terms<P>>> {
|
||||||
val balances = HashMap<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>>()
|
val balances = HashMap<Pair<AbstractParty, AbstractParty>, Amount<Obligation.Terms<P>>>()
|
||||||
|
|
||||||
states.forEach { state ->
|
states.forEach { state ->
|
||||||
val key = Pair(state.obligor.owningKey, state.beneficiary)
|
val key = Pair(state.obligor, state.beneficiary)
|
||||||
val balance = balances[key] ?: Amount(0L, product)
|
val balance = balances[key] ?: Amount(0L, product)
|
||||||
balances[key] = balance + Amount(state.amount.quantity, state.amount.token.product)
|
balances[key] = balance + Amount(state.amount.quantity, state.amount.token.product)
|
||||||
}
|
}
|
||||||
@ -645,8 +670,8 @@ fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<O
|
|||||||
/**
|
/**
|
||||||
* Net off the amounts due between parties.
|
* Net off the amounts due between parties.
|
||||||
*/
|
*/
|
||||||
fun <P : Any> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<Pair<PublicKey, PublicKey>, Amount<P>> {
|
fun <P: AbstractParty, T : Any> netAmountsDue(balances: Map<Pair<P, P>, Amount<T>>): Map<Pair<P, P>, Amount<T>> {
|
||||||
val nettedBalances = HashMap<Pair<PublicKey, PublicKey>, Amount<P>>()
|
val nettedBalances = HashMap<Pair<P, P>, Amount<T>>()
|
||||||
|
|
||||||
balances.forEach { balance ->
|
balances.forEach { balance ->
|
||||||
val (obligor, beneficiary) = balance.key
|
val (obligor, beneficiary) = balance.key
|
||||||
@ -669,9 +694,11 @@ fun <P : Any> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>
|
|||||||
*
|
*
|
||||||
* @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being
|
* @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being
|
||||||
* returned.
|
* returned.
|
||||||
|
* @param P type of party to operate on.
|
||||||
|
* @param T token that balances represent
|
||||||
*/
|
*/
|
||||||
fun <P : Any> sumAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<PublicKey, Long> {
|
fun <P: AbstractParty, T : Any> sumAmountsDue(balances: Map<Pair<P, P>, Amount<T>>): Map<P, Long> {
|
||||||
val sum = HashMap<PublicKey, Long>()
|
val sum = HashMap<P, Long>()
|
||||||
|
|
||||||
// Fill the map with zeroes initially
|
// Fill the map with zeroes initially
|
||||||
balances.keys.forEach {
|
balances.keys.forEach {
|
||||||
@ -712,11 +739,11 @@ fun <P : Any> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<O
|
|||||||
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
|
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
|
||||||
|
|
||||||
infix fun <T : Any> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
|
infix fun <T : Any> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
|
||||||
infix fun <T : Any> Obligation.State<T>.between(parties: Pair<AbstractParty, PublicKey>) = copy(obligor = parties.first.toAnonymous(), beneficiary = parties.second)
|
infix fun <T : Any> Obligation.State<T>.between(parties: Pair<AbstractParty, AbstractParty>) = copy(obligor = parties.first, beneficiary = parties.second)
|
||||||
infix fun <T : Any> Obligation.State<T>.`owned by`(owner: PublicKey) = copy(beneficiary = owner)
|
infix fun <T : Any> Obligation.State<T>.`owned by`(owner: AbstractParty) = copy(beneficiary = owner)
|
||||||
infix fun <T : Any> Obligation.State<T>.`issued by`(party: AbstractParty) = copy(obligor = party.toAnonymous())
|
infix fun <T : Any> Obligation.State<T>.`issued by`(party: AbstractParty) = copy(obligor = party)
|
||||||
// For Java users:
|
// For Java users:
|
||||||
@Suppress("unused") fun <T : Any> Obligation.State<T>.ownedBy(owner: PublicKey) = copy(beneficiary = owner)
|
@Suppress("unused") fun <T : Any> Obligation.State<T>.ownedBy(owner: AbstractParty) = copy(beneficiary = owner)
|
||||||
|
|
||||||
@Suppress("unused") fun <T : Any> Obligation.State<T>.issuedBy(party: AnonymousParty) = copy(obligor = party)
|
@Suppress("unused") fun <T : Any> Obligation.State<T>.issuedBy(party: AnonymousParty) = copy(obligor = party)
|
||||||
|
|
||||||
@ -728,4 +755,4 @@ val DUMMY_OBLIGATION_ISSUER by lazy { Party(X500Name("CN=Snake Oil Issuer,O=R3,O
|
|||||||
val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
|
val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
|
||||||
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
|
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
|
||||||
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
|
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
|
||||||
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER.toAnonymous(), token.OBLIGATION_DEF, quantity, NullPublicKey)
|
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NULL_PARTY)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.contracts.asset
|
package net.corda.contracts.asset
|
||||||
|
|
||||||
import net.corda.contracts.clause.AbstractConserveAmount
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
@ -51,9 +51,9 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <S : FungibleAsset<T>, T: Any> generateSpend(tx: TransactionBuilder,
|
fun <S : FungibleAsset<T>, T: Any> generateSpend(tx: TransactionBuilder,
|
||||||
amount: Amount<T>,
|
amount: Amount<T>,
|
||||||
to: PublicKey,
|
to: AbstractParty,
|
||||||
acceptableStates: List<StateAndRef<S>>,
|
acceptableStates: List<StateAndRef<S>>,
|
||||||
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>,
|
deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
|
||||||
generateMoveCommand: () -> CommandData): Pair<TransactionBuilder, List<PublicKey>> {
|
generateMoveCommand: () -> CommandData): Pair<TransactionBuilder, List<PublicKey>> {
|
||||||
// Discussion
|
// Discussion
|
||||||
//
|
//
|
||||||
@ -90,7 +90,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val keysUsed = gathered.map { it.state.data.owner }
|
val keysUsed = gathered.map { it.state.data.owner.owningKey }
|
||||||
|
|
||||||
val states = gathered.groupBy { it.state.data.amount.token.issuer }.map {
|
val states = gathered.groupBy { it.state.data.amount.token.issuer }.map {
|
||||||
val coins = it.value
|
val coins = it.value
|
||||||
@ -165,7 +165,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun <S : FungibleAsset<T>, T: Any> generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
fun <S : FungibleAsset<T>, T: Any> generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
||||||
assetStates: List<StateAndRef<S>>,
|
assetStates: List<StateAndRef<S>>,
|
||||||
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>,
|
deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
|
||||||
generateMoveCommand: () -> CommandData,
|
generateMoveCommand: () -> CommandData,
|
||||||
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey {
|
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey {
|
||||||
val owner = assetStates.map { it.state.data.owner }.toSet().singleOrNull() ?: throw InsufficientBalanceException(amountIssued)
|
val owner = assetStates.map { it.state.data.owner }.toSet().singleOrNull() ?: throw InsufficientBalanceException(amountIssued)
|
||||||
@ -193,7 +193,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
|
|
||||||
for (state in gathered) tx.addInputState(state)
|
for (state in gathered) tx.addInputState(state)
|
||||||
for (state in outputs) tx.addOutputState(state)
|
for (state in outputs) tx.addOutputState(state)
|
||||||
tx.addCommand(generateMoveCommand(), gathered.map { it.state.data.owner })
|
tx.addCommand(generateMoveCommand(), gathered.map { it.state.data.owner.owningKey })
|
||||||
tx.addCommand(generateExitCommand(amountIssued), gathered.flatMap { it.state.data.exitKeys })
|
tx.addCommand(generateExitCommand(amountIssued), gathered.flatMap { it.state.data.exitKeys })
|
||||||
return amountIssued.token.issuer.party.owningKey
|
return amountIssued.token.issuer.party.owningKey
|
||||||
}
|
}
|
||||||
@ -250,5 +250,5 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
|
|||||||
* implementations to have fields in their state which we don't know about here, and we simply leave them untouched
|
* implementations to have fields in their state which we don't know about here, and we simply leave them untouched
|
||||||
* when sending out "change" from spending/exiting.
|
* when sending out "change" from spending/exiting.
|
||||||
*/
|
*/
|
||||||
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKey): TransactionState<S>
|
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: AbstractParty): TransactionState<S>
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.contracts.clause
|
|||||||
import net.corda.contracts.asset.OnLedgerAsset
|
import net.corda.contracts.asset.OnLedgerAsset
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.Clause
|
import net.corda.core.contracts.clauses.Clause
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -31,7 +32,7 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
|
|||||||
@Throws(InsufficientBalanceException::class)
|
@Throws(InsufficientBalanceException::class)
|
||||||
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
||||||
assetStates: List<StateAndRef<S>>,
|
assetStates: List<StateAndRef<S>>,
|
||||||
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>,
|
deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S>,
|
||||||
generateMoveCommand: () -> CommandData,
|
generateMoveCommand: () -> CommandData,
|
||||||
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey
|
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey
|
||||||
= OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand)
|
= OnLedgerAsset.generateExit(tx, amountIssued, assetStates, deriveState, generateMoveCommand, generateExitCommand)
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.contracts.asset.extractAmountsDue
|
|||||||
import net.corda.contracts.asset.sumAmountsDue
|
import net.corda.contracts.asset.sumAmountsDue
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.Clause
|
import net.corda.core.contracts.clauses.Clause
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,7 +23,7 @@ interface NetState<P : Any> {
|
|||||||
* Bilateral states are used in close-out netting.
|
* Bilateral states are used in close-out netting.
|
||||||
*/
|
*/
|
||||||
data class BilateralNetState<P : Any>(
|
data class BilateralNetState<P : Any>(
|
||||||
val partyKeys: Set<PublicKey>,
|
val partyKeys: Set<AbstractParty>,
|
||||||
override val template: Obligation.Terms<P>
|
override val template: Obligation.Terms<P>
|
||||||
) : NetState<P>
|
) : NetState<P>
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ open class NetClause<C : CommandData, P : Any> : Clause<ContractState, C, Unit>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle proxies nominated by parties, i.e. a central clearing service
|
// TODO: Handle proxies nominated by parties, i.e. a central clearing service
|
||||||
val involvedParties = inputs.map { it.beneficiary }.union(inputs.map { it.obligor.owningKey }).toSet()
|
val involvedParties: Set<PublicKey> = inputs.map { it.beneficiary.owningKey }.union(inputs.map { it.obligor.owningKey }).toSet()
|
||||||
when (command.value.type) {
|
when (command.value.type) {
|
||||||
// For close-out netting, allow any involved party to sign
|
// For close-out netting, allow any involved party to sign
|
||||||
NetType.CLOSE_OUT -> require(command.signers.intersect(involvedParties).isNotEmpty()) { "any involved party has signed" }
|
NetType.CLOSE_OUT -> require(command.signers.intersect(involvedParties).isNotEmpty()) { "any involved party has signed" }
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.core.contracts.DealState
|
|||||||
import net.corda.core.contracts.TransactionForContract
|
import net.corda.core.contracts.TransactionForContract
|
||||||
import net.corda.core.contracts.UniqueIdentifier
|
import net.corda.core.contracts.UniqueIdentifier
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
@ -17,12 +18,12 @@ class DummyDealContract : Contract {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
override val contract: Contract = DummyDealContract(),
|
override val contract: Contract = DummyDealContract(),
|
||||||
override val participants: List<PublicKey> = listOf(),
|
override val participants: List<AbstractParty> = listOf(),
|
||||||
override val linearId: UniqueIdentifier = UniqueIdentifier(),
|
override val linearId: UniqueIdentifier = UniqueIdentifier(),
|
||||||
override val ref: String,
|
override val ref: String,
|
||||||
override val parties: List<AnonymousParty> = listOf()) : DealState {
|
override val parties: List<AnonymousParty> = listOf()) : DealState {
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return participants.any { it.containsAny(ourKeys) }
|
return participants.any { it.owningKey.containsAny(ourKeys) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateAgreement(notary: Party): TransactionBuilder {
|
override fun generateAgreement(notary: Party): TransactionBuilder {
|
||||||
|
@ -4,9 +4,9 @@ import net.corda.core.contracts.*
|
|||||||
import net.corda.core.contracts.clauses.Clause
|
import net.corda.core.contracts.clauses.Clause
|
||||||
import net.corda.core.contracts.clauses.FilterOn
|
import net.corda.core.contracts.clauses.FilterOn
|
||||||
import net.corda.core.contracts.clauses.verifyClause
|
import net.corda.core.contracts.clauses.verifyClause
|
||||||
import net.corda.core.crypto.CompositeKey
|
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.containsAny
|
import net.corda.core.crypto.containsAny
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
class DummyLinearContract : Contract {
|
class DummyLinearContract : Contract {
|
||||||
@ -20,11 +20,11 @@ class DummyLinearContract : Contract {
|
|||||||
data class State(
|
data class State(
|
||||||
override val linearId: UniqueIdentifier = UniqueIdentifier(),
|
override val linearId: UniqueIdentifier = UniqueIdentifier(),
|
||||||
override val contract: Contract = DummyLinearContract(),
|
override val contract: Contract = DummyLinearContract(),
|
||||||
override val participants: List<PublicKey> = listOf(),
|
override val participants: List<AbstractParty> = listOf(),
|
||||||
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
|
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
|
||||||
|
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return participants.any { it.containsAny(ourKeys) }
|
return participants.any { it.owningKey.containsAny(ourKeys) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,8 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
@ -21,13 +22,15 @@ import java.util.*
|
|||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
|
fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
|
||||||
revisions: Int? = 0,
|
revisions: Int? = 0,
|
||||||
participants: List<PublicKey> = emptyList()) : Vault<DealState> {
|
participants: List<AbstractParty> = emptyList()) : Vault<DealState> {
|
||||||
val freshKey = keyManagementService.freshKey()
|
val freshKey = keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
|
val recipient = AnonymousParty(freshPublicKey)
|
||||||
|
|
||||||
val transactions: List<SignedTransaction> = dealIds.map {
|
val transactions: List<SignedTransaction> = dealIds.map {
|
||||||
// Issue a deal state
|
// Issue a deal state
|
||||||
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(freshKey.public)))
|
addOutputState(DummyDealContract.State(ref = it, participants = participants.plus(recipient)))
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}
|
}
|
||||||
@ -47,13 +50,15 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>,
|
|||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
|
fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int,
|
||||||
uid: UniqueIdentifier = UniqueIdentifier(),
|
uid: UniqueIdentifier = UniqueIdentifier(),
|
||||||
participants: List<PublicKey> = emptyList()) : Vault<LinearState> {
|
participants: List<AbstractParty> = emptyList()) : Vault<LinearState> {
|
||||||
val freshKey = keyManagementService.freshKey()
|
val freshKey = keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
|
val recipient = AnonymousParty(freshPublicKey)
|
||||||
|
|
||||||
val transactions: List<SignedTransaction> = (1..numberToCreate).map {
|
val transactions: List<SignedTransaction> = (1..numberToCreate).map {
|
||||||
// Issue a Linear state
|
// Issue a Linear state
|
||||||
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(linearId = uid, participants = participants.plus(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = uid, participants = participants.plus(recipient)))
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}
|
}
|
||||||
@ -87,18 +92,19 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
|
|||||||
atMostThisManyStates: Int = 10,
|
atMostThisManyStates: Int = 10,
|
||||||
rng: Random = Random(),
|
rng: Random = Random(),
|
||||||
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
|
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
|
||||||
ownedBy: PublicKey? = null,
|
ownedBy: AbstractParty? = null,
|
||||||
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
|
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
|
||||||
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault<Cash.State> {
|
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault<Cash.State> {
|
||||||
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
|
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
|
||||||
|
|
||||||
val myKey: PublicKey = ownedBy ?: myInfo.legalIdentity.owningKey
|
val myKey: PublicKey = ownedBy?.owningKey ?: myInfo.legalIdentity.owningKey
|
||||||
|
val me = AnonymousParty(myKey)
|
||||||
|
|
||||||
// We will allocate one state to one transaction, for simplicities sake.
|
// We will allocate one state to one transaction, for simplicities sake.
|
||||||
val cash = Cash()
|
val cash = Cash()
|
||||||
val transactions: List<SignedTransaction> = amounts.map { pennies ->
|
val transactions: List<SignedTransaction> = amounts.map { pennies ->
|
||||||
val issuance = TransactionType.General.Builder(null)
|
val issuance = TransactionType.General.Builder(null)
|
||||||
cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy.copy(reference = ref), howMuch.token)), myKey, outputNotary)
|
cash.generateIssue(issuance, Amount(pennies, Issued(issuedBy.copy(reference = ref), howMuch.token)), me, outputNotary)
|
||||||
issuance.signWith(issuerKey)
|
issuance.signWith(issuerKey)
|
||||||
|
|
||||||
return@map issuance.toSignedTransaction(true)
|
return@map issuance.toSignedTransaction(true)
|
||||||
|
@ -60,7 +60,7 @@ class CashExitFlow(val amount: Amount<Currency>, val issueRef: OpaqueBytes, prog
|
|||||||
// count as a reason to fail?
|
// count as a reason to fail?
|
||||||
val participants: Set<Party> = inputStates
|
val participants: Set<Party> = inputStates
|
||||||
.filterIsInstance<Cash.State>()
|
.filterIsInstance<Cash.State>()
|
||||||
.map { serviceHub.identityService.partyFromKey(it.owner) }
|
.map { serviceHub.identityService.partyFromAnonymous(it.owner) }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ class CashIssueFlow(val amount: Amount<Currency>,
|
|||||||
progressTracker.currentStep = GENERATING_TX
|
progressTracker.currentStep = GENERATING_TX
|
||||||
val builder: TransactionBuilder = TransactionType.General.Builder(notary = null)
|
val builder: TransactionBuilder = TransactionType.General.Builder(notary = null)
|
||||||
val issuer = serviceHub.myInfo.legalIdentity.ref(issueRef)
|
val issuer = serviceHub.myInfo.legalIdentity.ref(issueRef)
|
||||||
Cash().generateIssue(builder, amount.issuedBy(issuer), recipient.owningKey, notary)
|
// TODO: Get a transaction key, don't just re-use the owning key
|
||||||
|
Cash().generateIssue(builder, amount.issuedBy(issuer), recipient, notary)
|
||||||
progressTracker.currentStep = SIGNING_TX
|
progressTracker.currentStep = SIGNING_TX
|
||||||
val myKey = serviceHub.legalIdentityKey
|
val myKey = serviceHub.legalIdentityKey
|
||||||
builder.signWith(myKey)
|
builder.signWith(myKey)
|
||||||
|
@ -39,7 +39,8 @@ open class CashPaymentFlow(
|
|||||||
serviceHub.vaultService.generateSpend(
|
serviceHub.vaultService.generateSpend(
|
||||||
builder,
|
builder,
|
||||||
amount,
|
amount,
|
||||||
recipient.owningKey,
|
// TODO: Get a transaction key, don't just re-use the owning key
|
||||||
|
recipient,
|
||||||
issuerConstraint)
|
issuerConstraint)
|
||||||
} catch (e: InsufficientBalanceException) {
|
} catch (e: InsufficientBalanceException) {
|
||||||
throw CashException("Insufficient cash for spend: ${e.message}", e)
|
throw CashException("Insufficient cash for spend: ${e.message}", e)
|
||||||
|
@ -8,6 +8,7 @@ import net.corda.core.crypto.expandedCompositeKeys
|
|||||||
import net.corda.core.crypto.sign
|
import net.corda.core.crypto.sign
|
||||||
import net.corda.core.flows.FlowException
|
import net.corda.core.flows.FlowException
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.seconds
|
import net.corda.core.seconds
|
||||||
@ -34,7 +35,7 @@ import java.util.*
|
|||||||
* 3. S signs it and commits it to the ledger, notarising it and distributing the final signed transaction back
|
* 3. S signs it and commits it to the ledger, notarising it and distributing the final signed transaction back
|
||||||
* to B.
|
* to B.
|
||||||
*
|
*
|
||||||
* Assuming no malicious termination, they both end the flow being in posession of a valid, signed transaction
|
* Assuming no malicious termination, they both end the flow being in possession of a valid, signed transaction
|
||||||
* that represents an atomic asset swap.
|
* that represents an atomic asset swap.
|
||||||
*
|
*
|
||||||
* Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
|
* Note that it's the *seller* who initiates contact with the buyer, not vice-versa as you might imagine.
|
||||||
@ -118,7 +119,7 @@ object TwoPartyTradeFlow {
|
|||||||
// even though it is missing signatures.
|
// even though it is missing signatures.
|
||||||
subFlow(ResolveTransactionsFlow(wtx, otherParty))
|
subFlow(ResolveTransactionsFlow(wtx, otherParty))
|
||||||
|
|
||||||
if (wtx.outputs.map { it.data }.sumCashBy(myPublicKey).withoutIssuer() != price)
|
if (wtx.outputs.map { it.data }.sumCashBy(AnonymousParty(myPublicKey)).withoutIssuer() != price)
|
||||||
throw FlowException("Transaction is not sending us the right amount of cash")
|
throw FlowException("Transaction is not sending us the right amount of cash")
|
||||||
|
|
||||||
it
|
it
|
||||||
@ -219,7 +220,7 @@ object TwoPartyTradeFlow {
|
|||||||
val ptx = TransactionType.General.Builder(notary)
|
val ptx = TransactionType.General.Builder(notary)
|
||||||
|
|
||||||
// Add input and output states for the movement of cash, by using the Cash contract to generate the states
|
// Add input and output states for the movement of cash, by using the Cash contract to generate the states
|
||||||
val (tx, cashSigningPubKeys) = serviceHub.vaultService.generateSpend(ptx, tradeRequest.price, tradeRequest.sellerOwnerKey)
|
val (tx, cashSigningPubKeys) = serviceHub.vaultService.generateSpend(ptx, tradeRequest.price, AnonymousParty(tradeRequest.sellerOwnerKey))
|
||||||
|
|
||||||
// Add inputs/outputs/a command for the movement of the asset.
|
// Add inputs/outputs/a command for the movement of the asset.
|
||||||
tx.addInputState(tradeRequest.assetForSale)
|
tx.addInputState(tradeRequest.assetForSale)
|
||||||
@ -228,10 +229,10 @@ object TwoPartyTradeFlow {
|
|||||||
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
|
// we want for privacy reasons: the key is here ONLY to manage and control ownership, it is not intended to
|
||||||
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
|
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
|
||||||
// initial seed in order to provide privacy protection.
|
// initial seed in order to provide privacy protection.
|
||||||
val freshKey = serviceHub.keyManagementService.freshKey()
|
val freshPublicKey = serviceHub.keyManagementService.freshKey().public
|
||||||
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public)
|
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(AnonymousParty(freshPublicKey))
|
||||||
tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
|
tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
|
||||||
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner)
|
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner.owningKey)
|
||||||
|
|
||||||
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
|
// And add a request for timestamping: it may be that none of the contracts need this! But it can't hurt
|
||||||
// to have one.
|
// to have one.
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package net.corda.contracts.asset;
|
package net.corda.contracts.asset;
|
||||||
|
|
||||||
import kotlin.*;
|
import kotlin.Unit;
|
||||||
import net.corda.core.contracts.*;
|
import net.corda.core.contracts.PartyAndReference;
|
||||||
import net.corda.core.serialization.*;
|
import net.corda.core.identity.AnonymousParty;
|
||||||
import org.junit.*;
|
import net.corda.core.serialization.OpaqueBytes;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import static net.corda.core.contracts.ContractsDSL.*;
|
import static net.corda.core.contracts.ContractsDSL.DOLLARS;
|
||||||
import static net.corda.core.utilities.TestConstants.*;
|
import static net.corda.core.contracts.ContractsDSL.issuedBy;
|
||||||
|
import static net.corda.core.utilities.TestConstants.getDUMMY_PUBKEY_1;
|
||||||
|
import static net.corda.core.utilities.TestConstants.getDUMMY_PUBKEY_2;
|
||||||
import static net.corda.testing.CoreTestUtils.*;
|
import static net.corda.testing.CoreTestUtils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,8 +18,8 @@ import static net.corda.testing.CoreTestUtils.*;
|
|||||||
public class CashTestsJava {
|
public class CashTestsJava {
|
||||||
private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{1});
|
private final OpaqueBytes defaultRef = new OpaqueBytes(new byte[]{1});
|
||||||
private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef);
|
private final PartyAndReference defaultIssuer = getMEGA_CORP().ref(defaultRef);
|
||||||
private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), getDUMMY_PUBKEY_1());
|
private final Cash.State inState = new Cash.State(issuedBy(DOLLARS(1000), defaultIssuer), new AnonymousParty(getDUMMY_PUBKEY_1()));
|
||||||
private final Cash.State outState = new Cash.State(inState.getAmount(), getDUMMY_PUBKEY_2());
|
private final Cash.State outState = new Cash.State(inState.getAmount(), new AnonymousParty(getDUMMY_PUBKEY_2()));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void trivial() {
|
public void trivial() {
|
||||||
@ -26,7 +29,7 @@ public class CashTestsJava {
|
|||||||
tx.failsWith("the amounts balance");
|
tx.failsWith("the amounts balance");
|
||||||
|
|
||||||
tx.tweak(tw -> {
|
tx.tweak(tw -> {
|
||||||
tw.output(new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), getDUMMY_PUBKEY_2()));
|
tw.output(new Cash.State(issuedBy(DOLLARS(2000), defaultIssuer), new AnonymousParty(getDUMMY_PUBKEY_2())));
|
||||||
return tw.failsWith("the amounts balance");
|
return tw.failsWith("the amounts balance");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,15 +4,13 @@ import net.corda.contracts.asset.*
|
|||||||
import net.corda.contracts.testing.fillWithSomeTestCash
|
import net.corda.contracts.testing.fillWithSomeTestCash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.days
|
import net.corda.core.days
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.seconds
|
import net.corda.core.seconds
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.*
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_1
|
|
||||||
import net.corda.core.utilities.TEST_TX_TIME
|
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.node.utilities.transaction
|
import net.corda.node.utilities.transaction
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
@ -38,7 +36,7 @@ interface ICommercialPaperTestTemplate {
|
|||||||
class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
|
class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
|
||||||
override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State(
|
override fun getPaper(): ICommercialPaperState = JavaCommercialPaper.State(
|
||||||
MEGA_CORP.ref(123),
|
MEGA_CORP.ref(123),
|
||||||
MEGA_CORP_PUBKEY,
|
MEGA_CORP,
|
||||||
1000.DOLLARS `issued by` MEGA_CORP.ref(123),
|
1000.DOLLARS `issued by` MEGA_CORP.ref(123),
|
||||||
TEST_TX_TIME + 7.days
|
TEST_TX_TIME + 7.days
|
||||||
)
|
)
|
||||||
@ -51,7 +49,7 @@ class JavaCommercialPaperTest : ICommercialPaperTestTemplate {
|
|||||||
class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
|
class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
|
||||||
override fun getPaper(): ICommercialPaperState = CommercialPaper.State(
|
override fun getPaper(): ICommercialPaperState = CommercialPaper.State(
|
||||||
issuance = MEGA_CORP.ref(123),
|
issuance = MEGA_CORP.ref(123),
|
||||||
owner = MEGA_CORP_PUBKEY,
|
owner = MEGA_CORP,
|
||||||
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
|
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
|
||||||
maturityDate = TEST_TX_TIME + 7.days
|
maturityDate = TEST_TX_TIME + 7.days
|
||||||
)
|
)
|
||||||
@ -64,7 +62,7 @@ class KotlinCommercialPaperTest : ICommercialPaperTestTemplate {
|
|||||||
class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate {
|
class KotlinCommercialPaperLegacyTest : ICommercialPaperTestTemplate {
|
||||||
override fun getPaper(): ICommercialPaperState = CommercialPaperLegacy.State(
|
override fun getPaper(): ICommercialPaperState = CommercialPaperLegacy.State(
|
||||||
issuance = MEGA_CORP.ref(123),
|
issuance = MEGA_CORP.ref(123),
|
||||||
owner = MEGA_CORP_PUBKEY,
|
owner = MEGA_CORP,
|
||||||
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
|
faceValue = 1000.DOLLARS `issued by` MEGA_CORP.ref(123),
|
||||||
maturityDate = TEST_TX_TIME + 7.days
|
maturityDate = TEST_TX_TIME + 7.days
|
||||||
)
|
)
|
||||||
@ -91,8 +89,8 @@ class CommercialPaperTestsGeneric {
|
|||||||
val someProfits = 1200.DOLLARS `issued by` issuer
|
val someProfits = 1200.DOLLARS `issued by` issuer
|
||||||
ledger {
|
ledger {
|
||||||
unverifiedTransaction {
|
unverifiedTransaction {
|
||||||
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
|
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE)
|
||||||
output("some profits", someProfits.STATE `owned by` MEGA_CORP_PUBKEY)
|
output("some profits", someProfits.STATE `owned by` MEGA_CORP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some CP is issued onto the ledger by MegaCorp.
|
// Some CP is issued onto the ledger by MegaCorp.
|
||||||
@ -108,8 +106,8 @@ class CommercialPaperTestsGeneric {
|
|||||||
transaction("Trade") {
|
transaction("Trade") {
|
||||||
input("paper")
|
input("paper")
|
||||||
input("alice's $900")
|
input("alice's $900")
|
||||||
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP }
|
||||||
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE_PUBKEY }
|
output("alice's paper") { "paper".output<ICommercialPaperState>() `owned by` ALICE }
|
||||||
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
|
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -122,8 +120,8 @@ class CommercialPaperTestsGeneric {
|
|||||||
input("some profits")
|
input("some profits")
|
||||||
|
|
||||||
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
|
fun TransactionDSL<TransactionDSLInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
|
||||||
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY }
|
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE }
|
||||||
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY }
|
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP }
|
||||||
}
|
}
|
||||||
|
|
||||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
@ -270,8 +268,8 @@ class CommercialPaperTestsGeneric {
|
|||||||
// Alice pays $9000 to BigCorp to own some of their debt.
|
// Alice pays $9000 to BigCorp to own some of their debt.
|
||||||
moveTX = run {
|
moveTX = run {
|
||||||
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public)
|
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, AnonymousParty(bigCorpServices.key.public))
|
||||||
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public)
|
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), AnonymousParty(aliceServices.key.public))
|
||||||
ptx.signWith(bigCorpServices.key)
|
ptx.signWith(bigCorpServices.key)
|
||||||
ptx.signWith(aliceServices.key)
|
ptx.signWith(aliceServices.key)
|
||||||
ptx.signWith(DUMMY_NOTARY_KEY)
|
ptx.signWith(DUMMY_NOTARY_KEY)
|
||||||
|
@ -2,8 +2,10 @@ package net.corda.contracts.asset
|
|||||||
|
|
||||||
import net.corda.contracts.testing.fillWithSomeTestCash
|
import net.corda.contracts.testing.fillWithSomeTestCash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.generateKeyPair
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.node.services.unconsumedStates
|
import net.corda.core.node.services.unconsumedStates
|
||||||
@ -23,7 +25,6 @@ import org.junit.Before
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -32,11 +33,11 @@ class CashTests {
|
|||||||
val defaultIssuer = MEGA_CORP.ref(defaultRef)
|
val defaultIssuer = MEGA_CORP.ref(defaultRef)
|
||||||
val inState = Cash.State(
|
val inState = Cash.State(
|
||||||
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
amount = 1000.DOLLARS `issued by` defaultIssuer,
|
||||||
owner = DUMMY_PUBKEY_1
|
owner = AnonymousParty(DUMMY_PUBKEY_1)
|
||||||
)
|
)
|
||||||
// Input state held by the issuer
|
// Input state held by the issuer
|
||||||
val issuerInState = inState.copy(owner = defaultIssuer.party.owningKey)
|
val issuerInState = inState.copy(owner = defaultIssuer.party)
|
||||||
val outState = issuerInState.copy(owner = DUMMY_PUBKEY_2)
|
val outState = issuerInState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2))
|
||||||
|
|
||||||
fun Cash.State.editDepositRef(ref: Byte) = copy(
|
fun Cash.State.editDepositRef(ref: Byte) = copy(
|
||||||
amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref))))
|
amount = Amount(amount.quantity, token = amount.token.copy(amount.token.issuer.copy(reference = OpaqueBytes.of(ref))))
|
||||||
@ -70,13 +71,13 @@ class CashTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
services.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
services.fillWithSomeTestCash(howMuch = 100.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_PUBKEY_1)
|
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1)
|
||||||
services.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
services.fillWithSomeTestCash(howMuch = 400.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_PUBKEY_1)
|
issuedBy = MEGA_CORP.ref(1), issuerKey = MEGA_CORP_KEY, ownedBy = OUR_IDENTITY_1)
|
||||||
services.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
services.fillWithSomeTestCash(howMuch = 80.DOLLARS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_PUBKEY_1)
|
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1)
|
||||||
services.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
services.fillWithSomeTestCash(howMuch = 80.SWISS_FRANCS, atLeastThisManyStates = 1, atMostThisManyStates = 1,
|
||||||
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_PUBKEY_1)
|
issuedBy = MINI_CORP.ref(1), issuerKey = MINI_CORP_KEY, ownedBy = OUR_IDENTITY_1)
|
||||||
|
|
||||||
vaultStatesUnconsumed = services.vaultService.unconsumedStates<Cash.State>().toList()
|
vaultStatesUnconsumed = services.vaultService.unconsumedStates<Cash.State>().toList()
|
||||||
}
|
}
|
||||||
@ -142,7 +143,7 @@ class CashTests {
|
|||||||
output {
|
output {
|
||||||
Cash.State(
|
Cash.State(
|
||||||
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
|
amount = 1000.DOLLARS `issued by` MINI_CORP.ref(12, 34),
|
||||||
owner = DUMMY_PUBKEY_1
|
owner = AnonymousParty(DUMMY_PUBKEY_1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
tweak {
|
tweak {
|
||||||
@ -158,14 +159,14 @@ class CashTests {
|
|||||||
fun generateIssueRaw() {
|
fun generateIssueRaw() {
|
||||||
// Test generation works.
|
// Test generation works.
|
||||||
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
|
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
|
||||||
signWith(MINI_CORP_KEY)
|
signWith(MINI_CORP_KEY)
|
||||||
}.toSignedTransaction().tx
|
}.toSignedTransaction().tx
|
||||||
assertTrue(tx.inputs.isEmpty())
|
assertTrue(tx.inputs.isEmpty())
|
||||||
val s = tx.outputs[0].data as Cash.State
|
val s = tx.outputs[0].data as Cash.State
|
||||||
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
|
assertEquals(100.DOLLARS `issued by` MINI_CORP.ref(12, 34), s.amount)
|
||||||
assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
|
assertEquals(MINI_CORP as AbstractParty, s.amount.token.issuer.party)
|
||||||
assertEquals(DUMMY_PUBKEY_1, s.owner)
|
assertEquals(AnonymousParty(DUMMY_PUBKEY_1), s.owner)
|
||||||
assertTrue(tx.commands[0].value is Cash.Commands.Issue)
|
assertTrue(tx.commands[0].value is Cash.Commands.Issue)
|
||||||
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
|
assertEquals(MINI_CORP_PUBKEY, tx.commands[0].signers[0])
|
||||||
}
|
}
|
||||||
@ -175,7 +176,7 @@ class CashTests {
|
|||||||
// Test issuance from an issued amount
|
// Test issuance from an issued amount
|
||||||
val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34)
|
val amount = 100.DOLLARS `issued by` MINI_CORP.ref(12, 34)
|
||||||
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
|
val tx: WireTransaction = TransactionType.General.Builder(notary = null).apply {
|
||||||
Cash().generateIssue(this, amount, owner = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY)
|
Cash().generateIssue(this, amount, owner = AnonymousParty(DUMMY_PUBKEY_1), notary = DUMMY_NOTARY)
|
||||||
signWith(MINI_CORP_KEY)
|
signWith(MINI_CORP_KEY)
|
||||||
}.toSignedTransaction().tx
|
}.toSignedTransaction().tx
|
||||||
assertTrue(tx.inputs.isEmpty())
|
assertTrue(tx.inputs.isEmpty())
|
||||||
@ -248,14 +249,14 @@ class CashTests {
|
|||||||
// Issue some cash
|
// Issue some cash
|
||||||
var ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
var ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
|
|
||||||
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
|
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY)
|
||||||
ptx.signWith(MINI_CORP_KEY)
|
ptx.signWith(MINI_CORP_KEY)
|
||||||
val tx = ptx.toSignedTransaction()
|
val tx = ptx.toSignedTransaction()
|
||||||
|
|
||||||
// Include the previously issued cash in a new issuance command
|
// Include the previously issued cash in a new issuance command
|
||||||
ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
ptx.addInputState(tx.tx.outRef<Cash.State>(0))
|
ptx.addInputState(tx.tx.outRef<Cash.State>(0))
|
||||||
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
|
Cash().generateIssue(ptx, 100.DOLLARS `issued by` MINI_CORP.ref(12, 34), owner = MINI_CORP, notary = DUMMY_NOTARY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -329,7 +330,7 @@ class CashTests {
|
|||||||
input {
|
input {
|
||||||
inState.copy(
|
inState.copy(
|
||||||
amount = 150.POUNDS `issued by` defaultIssuer,
|
amount = 150.POUNDS `issued by` defaultIssuer,
|
||||||
owner = DUMMY_PUBKEY_2
|
owner = AnonymousParty(DUMMY_PUBKEY_2)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
|
output { outState.copy(amount = 1150.DOLLARS `issued by` defaultIssuer) }
|
||||||
@ -382,10 +383,10 @@ class CashTests {
|
|||||||
// Multi-issuer case.
|
// Multi-issuer case.
|
||||||
transaction {
|
transaction {
|
||||||
input { issuerInState }
|
input { issuerInState }
|
||||||
input { issuerInState.copy(owner = MINI_CORP_PUBKEY) `issued by` MINI_CORP }
|
input { issuerInState.copy(owner = MINI_CORP) `issued by` MINI_CORP }
|
||||||
|
|
||||||
output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP }
|
output { issuerInState.copy(amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) `issued by` MINI_CORP }
|
||||||
output { issuerInState.copy(owner = MINI_CORP_PUBKEY, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
|
output { issuerInState.copy(owner = MINI_CORP, amount = issuerInState.amount - (200.DOLLARS `issued by` defaultIssuer)) }
|
||||||
|
|
||||||
command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
|
|
||||||
@ -420,19 +421,19 @@ class CashTests {
|
|||||||
|
|
||||||
// Can't merge them together.
|
// Can't merge them together.
|
||||||
tweak {
|
tweak {
|
||||||
output { inState.copy(owner = DUMMY_PUBKEY_2, amount = 2000.DOLLARS `issued by` defaultIssuer) }
|
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2), amount = 2000.DOLLARS `issued by` defaultIssuer) }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
// Missing MiniCorp deposit
|
// Missing MiniCorp deposit
|
||||||
tweak {
|
tweak {
|
||||||
output { inState.copy(owner = DUMMY_PUBKEY_2) }
|
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
|
||||||
output { inState.copy(owner = DUMMY_PUBKEY_2) }
|
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
|
|
||||||
// This works.
|
// This works.
|
||||||
output { inState.copy(owner = DUMMY_PUBKEY_2) }
|
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) }
|
||||||
output { inState.copy(owner = DUMMY_PUBKEY_2) `issued by` MINI_CORP }
|
output { inState.copy(owner = AnonymousParty(DUMMY_PUBKEY_2)) `issued by` MINI_CORP }
|
||||||
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
command(DUMMY_PUBKEY_1) { Cash.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
@ -442,11 +443,11 @@ class CashTests {
|
|||||||
fun multiCurrency() {
|
fun multiCurrency() {
|
||||||
// Check we can do an atomic currency trade tx.
|
// Check we can do an atomic currency trade tx.
|
||||||
transaction {
|
transaction {
|
||||||
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), DUMMY_PUBKEY_2)
|
val pounds = Cash.State(658.POUNDS `issued by` MINI_CORP.ref(3, 4, 5), AnonymousParty(DUMMY_PUBKEY_2))
|
||||||
input { inState `owned by` DUMMY_PUBKEY_1 }
|
input { inState `owned by` AnonymousParty(DUMMY_PUBKEY_1) }
|
||||||
input { pounds }
|
input { pounds }
|
||||||
output { inState `owned by` DUMMY_PUBKEY_2 }
|
output { inState `owned by` AnonymousParty(DUMMY_PUBKEY_2) }
|
||||||
output { pounds `owned by` DUMMY_PUBKEY_1 }
|
output { pounds `owned by` AnonymousParty(DUMMY_PUBKEY_1) }
|
||||||
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Cash.Commands.Move() }
|
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Cash.Commands.Move() }
|
||||||
|
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -458,13 +459,13 @@ class CashTests {
|
|||||||
// Spend tx generation
|
// Spend tx generation
|
||||||
|
|
||||||
val OUR_KEY: KeyPair by lazy { generateKeyPair() }
|
val OUR_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val OUR_PUBKEY_1: PublicKey get() = OUR_KEY.public
|
val OUR_IDENTITY_1: AbstractParty get() = AnonymousParty(OUR_KEY.public)
|
||||||
|
|
||||||
val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2
|
val THEIR_IDENTITY_1 = AnonymousParty(DUMMY_PUBKEY_2)
|
||||||
|
|
||||||
fun makeCash(amount: Amount<Currency>, corp: Party, depositRef: Byte = 1) =
|
fun makeCash(amount: Amount<Currency>, corp: Party, depositRef: Byte = 1) =
|
||||||
StateAndRef(
|
StateAndRef(
|
||||||
Cash.State(amount `issued by` corp.ref(depositRef), OUR_PUBKEY_1) `with notary` DUMMY_NOTARY,
|
Cash.State(amount `issued by` corp.ref(depositRef), OUR_IDENTITY_1) `with notary` DUMMY_NOTARY,
|
||||||
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -484,7 +485,7 @@ class CashTests {
|
|||||||
return tx.toWireTransaction()
|
return tx.toWireTransaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeSpend(amount: Amount<Currency>, dest: PublicKey): WireTransaction {
|
fun makeSpend(amount: Amount<Currency>, dest: AbstractParty): WireTransaction {
|
||||||
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
|
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
vault.generateSpend(tx, amount, dest)
|
vault.generateSpend(tx, amount, dest)
|
||||||
@ -566,13 +567,13 @@ class CashTests {
|
|||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
val wtx = makeSpend(100.DOLLARS, THEIR_PUBKEY_1)
|
val wtx = makeSpend(100.DOLLARS, THEIR_IDENTITY_1)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
||||||
assertEquals(vaultState.ref, wtx.inputs[0])
|
assertEquals(vaultState.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[0].data)
|
assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1), wtx.outputs[0].data)
|
||||||
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,7 +583,7 @@ class CashTests {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
|
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
vault.generateSpend(tx, 80.DOLLARS, ALICE_PUBKEY, setOf(MINI_CORP.toAnonymous()))
|
vault.generateSpend(tx, 80.DOLLARS, ALICE, setOf(MINI_CORP))
|
||||||
|
|
||||||
assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0])
|
assertEquals(vaultStatesUnconsumed.elementAt(2).ref, tx.inputStates()[0])
|
||||||
}
|
}
|
||||||
@ -593,14 +594,14 @@ class CashTests {
|
|||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
val wtx = makeSpend(10.DOLLARS, THEIR_PUBKEY_1)
|
val wtx = makeSpend(10.DOLLARS, THEIR_IDENTITY_1)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
val vaultState = vaultStatesUnconsumed.elementAt(0)
|
||||||
assertEquals(vaultState.ref, wtx.inputs[0])
|
assertEquals(vaultState.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState.state.data.copy(owner = THEIR_PUBKEY_1, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
|
assertEquals(vaultState.state.data.copy(owner = THEIR_IDENTITY_1, amount = 10.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
|
||||||
assertEquals(vaultState.state.data.copy(amount = 90.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
assertEquals(vaultState.state.data.copy(amount = 90.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
||||||
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -608,15 +609,15 @@ class CashTests {
|
|||||||
fun generateSpendWithTwoInputs() {
|
fun generateSpendWithTwoInputs() {
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val wtx = makeSpend(500.DOLLARS, THEIR_PUBKEY_1)
|
val wtx = makeSpend(500.DOLLARS, THEIR_IDENTITY_1)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val vaultState0 = vaultStatesUnconsumed.elementAt(0)
|
val vaultState0 = vaultStatesUnconsumed.elementAt(0)
|
||||||
val vaultState1 = vaultStatesUnconsumed.elementAt(1)
|
val vaultState1 = vaultStatesUnconsumed.elementAt(1)
|
||||||
assertEquals(vaultState0.ref, wtx.inputs[0])
|
assertEquals(vaultState0.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState1.ref, wtx.inputs[1])
|
assertEquals(vaultState1.ref, wtx.inputs[1])
|
||||||
assertEquals(vaultState0.state.data.copy(owner = THEIR_PUBKEY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
|
assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[0].data)
|
||||||
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +625,7 @@ class CashTests {
|
|||||||
fun generateSpendMixedDeposits() {
|
fun generateSpendMixedDeposits() {
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val wtx = makeSpend(580.DOLLARS, THEIR_PUBKEY_1)
|
val wtx = makeSpend(580.DOLLARS, THEIR_IDENTITY_1)
|
||||||
assertEquals(3, wtx.inputs.size)
|
assertEquals(3, wtx.inputs.size)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@ -635,9 +636,9 @@ class CashTests {
|
|||||||
assertEquals(vaultState0.ref, wtx.inputs[0])
|
assertEquals(vaultState0.ref, wtx.inputs[0])
|
||||||
assertEquals(vaultState1.ref, wtx.inputs[1])
|
assertEquals(vaultState1.ref, wtx.inputs[1])
|
||||||
assertEquals(vaultState2.ref, wtx.inputs[2])
|
assertEquals(vaultState2.ref, wtx.inputs[2])
|
||||||
assertEquals(vaultState0.state.data.copy(owner = THEIR_PUBKEY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
assertEquals(vaultState0.state.data.copy(owner = THEIR_IDENTITY_1, amount = 500.DOLLARS `issued by` defaultIssuer), wtx.outputs[1].data)
|
||||||
assertEquals(vaultState2.state.data.copy(owner = THEIR_PUBKEY_1), wtx.outputs[0].data)
|
assertEquals(vaultState2.state.data.copy(owner = THEIR_IDENTITY_1), wtx.outputs[0].data)
|
||||||
assertEquals(OUR_PUBKEY_1, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
assertEquals(OUR_IDENTITY_1.owningKey, wtx.commands.single { it.value is Cash.Commands.Move }.signers[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,12 +648,12 @@ class CashTests {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
val e: InsufficientBalanceException = assertFailsWith("balance") {
|
val e: InsufficientBalanceException = assertFailsWith("balance") {
|
||||||
makeSpend(1000.DOLLARS, THEIR_PUBKEY_1)
|
makeSpend(1000.DOLLARS, THEIR_IDENTITY_1)
|
||||||
}
|
}
|
||||||
assertEquals((1000 - 580).DOLLARS, e.amountMissing)
|
assertEquals((1000 - 580).DOLLARS, e.amountMissing)
|
||||||
|
|
||||||
assertFailsWith(InsufficientBalanceException::class) {
|
assertFailsWith(InsufficientBalanceException::class) {
|
||||||
makeSpend(81.SWISS_FRANCS, THEIR_PUBKEY_1)
|
makeSpend(81.SWISS_FRANCS, THEIR_IDENTITY_1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -662,9 +663,9 @@ class CashTests {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun aggregation() {
|
fun aggregation() {
|
||||||
val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` MEGA_CORP.ref(2), MEGA_CORP_PUBKEY)
|
val fiveThousandDollarsFromMega = Cash.State(5000.DOLLARS `issued by` MEGA_CORP.ref(2), MEGA_CORP)
|
||||||
val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` MEGA_CORP.ref(2), MINI_CORP_PUBKEY)
|
val twoThousandDollarsFromMega = Cash.State(2000.DOLLARS `issued by` MEGA_CORP.ref(2), MINI_CORP)
|
||||||
val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), MEGA_CORP_PUBKEY)
|
val oneThousandDollarsFromMini = Cash.State(1000.DOLLARS `issued by` MINI_CORP.ref(3), MEGA_CORP)
|
||||||
|
|
||||||
// Obviously it must be possible to aggregate states with themselves
|
// Obviously it must be possible to aggregate states with themselves
|
||||||
assertEquals(fiveThousandDollarsFromMega.amount.token, fiveThousandDollarsFromMega.amount.token)
|
assertEquals(fiveThousandDollarsFromMega.amount.token, fiveThousandDollarsFromMega.amount.token)
|
||||||
@ -678,7 +679,7 @@ class CashTests {
|
|||||||
|
|
||||||
// States cannot be aggregated if the currency differs
|
// States cannot be aggregated if the currency differs
|
||||||
assertNotEquals(oneThousandDollarsFromMini.amount.token,
|
assertNotEquals(oneThousandDollarsFromMini.amount.token,
|
||||||
Cash.State(1000.POUNDS `issued by` MINI_CORP.ref(3), MEGA_CORP_PUBKEY).amount.token)
|
Cash.State(1000.POUNDS `issued by` MINI_CORP.ref(3), MEGA_CORP).amount.token)
|
||||||
|
|
||||||
// States cannot be aggregated if the reference differs
|
// States cannot be aggregated if the reference differs
|
||||||
assertNotEquals(fiveThousandDollarsFromMega.amount.token, (fiveThousandDollarsFromMega `with deposit` defaultIssuer).amount.token)
|
assertNotEquals(fiveThousandDollarsFromMega.amount.token, (fiveThousandDollarsFromMega `with deposit` defaultIssuer).amount.token)
|
||||||
@ -688,20 +689,20 @@ class CashTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `summing by owner`() {
|
fun `summing by owner`() {
|
||||||
val states = listOf(
|
val states = listOf(
|
||||||
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP_PUBKEY),
|
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MINI_CORP),
|
||||||
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
|
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
|
||||||
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY)
|
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
|
||||||
)
|
)
|
||||||
assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(MEGA_CORP_PUBKEY))
|
assertEquals(6000.DOLLARS `issued by` defaultIssuer, states.sumCashBy(MEGA_CORP))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = UnsupportedOperationException::class)
|
@Test(expected = UnsupportedOperationException::class)
|
||||||
fun `summing by owner throws`() {
|
fun `summing by owner throws`() {
|
||||||
val states = listOf(
|
val states = listOf(
|
||||||
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
|
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
|
||||||
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY)
|
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
|
||||||
)
|
)
|
||||||
states.sumCashBy(MINI_CORP_PUBKEY)
|
states.sumCashBy(MINI_CORP)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -720,9 +721,9 @@ class CashTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `summing a single currency`() {
|
fun `summing a single currency`() {
|
||||||
val states = listOf(
|
val states = listOf(
|
||||||
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
|
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
|
||||||
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
|
Cash.State(2000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
|
||||||
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY)
|
Cash.State(4000.DOLLARS `issued by` defaultIssuer, MEGA_CORP)
|
||||||
)
|
)
|
||||||
// Test that summing everything produces the total number of dollars
|
// Test that summing everything produces the total number of dollars
|
||||||
val expected = 7000.DOLLARS `issued by` defaultIssuer
|
val expected = 7000.DOLLARS `issued by` defaultIssuer
|
||||||
@ -733,8 +734,8 @@ class CashTests {
|
|||||||
@Test(expected = IllegalArgumentException::class)
|
@Test(expected = IllegalArgumentException::class)
|
||||||
fun `summing multiple currencies`() {
|
fun `summing multiple currencies`() {
|
||||||
val states = listOf(
|
val states = listOf(
|
||||||
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP_PUBKEY),
|
Cash.State(1000.DOLLARS `issued by` defaultIssuer, MEGA_CORP),
|
||||||
Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP_PUBKEY)
|
Cash.State(4000.POUNDS `issued by` defaultIssuer, MEGA_CORP)
|
||||||
)
|
)
|
||||||
// Test that summing everything fails because we're mixing units
|
// Test that summing everything fails because we're mixing units
|
||||||
states.sumCash()
|
states.sumCash()
|
||||||
@ -748,14 +749,14 @@ class CashTests {
|
|||||||
output("MEGA_CORP cash") {
|
output("MEGA_CORP cash") {
|
||||||
Cash.State(
|
Cash.State(
|
||||||
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
amount = 1000.DOLLARS `issued by` MEGA_CORP.ref(1, 1),
|
||||||
owner = MEGA_CORP_PUBKEY
|
owner = MEGA_CORP
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
input("MEGA_CORP cash")
|
input("MEGA_CORP cash")
|
||||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = DUMMY_PUBKEY_1))
|
output("MEGA_CORP cash".output<Cash.State>().copy(owner = AnonymousParty(DUMMY_PUBKEY_1)))
|
||||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
@ -764,7 +765,7 @@ class CashTests {
|
|||||||
transaction {
|
transaction {
|
||||||
input("MEGA_CORP cash")
|
input("MEGA_CORP cash")
|
||||||
// We send it to another pubkey so that the transaction is not identical to the previous one
|
// We send it to another pubkey so that the transaction is not identical to the previous one
|
||||||
output("MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE_PUBKEY))
|
output("MEGA_CORP cash".output<Cash.State>().copy(owner = ALICE))
|
||||||
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,14 @@ package net.corda.contracts.asset
|
|||||||
|
|
||||||
import net.corda.contracts.asset.Obligation.Lifecycle
|
import net.corda.contracts.asset.Obligation.Lifecycle
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.NullPublicKey
|
import net.corda.core.crypto.NULL_PARTY
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.serialization.OpaqueBytes
|
import net.corda.core.serialization.OpaqueBytes
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -33,18 +34,18 @@ class ObligationTests {
|
|||||||
obligor = MEGA_CORP,
|
obligor = MEGA_CORP,
|
||||||
template = megaCorpDollarSettlement,
|
template = megaCorpDollarSettlement,
|
||||||
quantity = 1000.DOLLARS.quantity,
|
quantity = 1000.DOLLARS.quantity,
|
||||||
beneficiary = DUMMY_PUBKEY_1
|
beneficiary = CHARLIE
|
||||||
)
|
)
|
||||||
val outState = inState.copy(beneficiary = DUMMY_PUBKEY_2)
|
val outState = inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2))
|
||||||
|
|
||||||
private fun cashObligationTestRoots(
|
private fun cashObligationTestRoots(
|
||||||
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
|
group: LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
|
||||||
) = group.apply {
|
) = group.apply {
|
||||||
unverifiedTransaction {
|
unverifiedTransaction {
|
||||||
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY))
|
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
|
||||||
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY))
|
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION between Pair(BOB, ALICE))
|
||||||
output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB_PUBKEY))
|
output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB))
|
||||||
output("Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY)
|
output("Alice's $1,000,000", 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,13 +72,13 @@ class ObligationTests {
|
|||||||
tweak {
|
tweak {
|
||||||
output { outState }
|
output { outState }
|
||||||
output { outState `issued by` MINI_CORP }
|
output { outState `issued by` MINI_CORP }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
this `fails with` "at least one asset input"
|
this `fails with` "at least one asset input"
|
||||||
}
|
}
|
||||||
// Simple reallocation works.
|
// Simple reallocation works.
|
||||||
tweak {
|
tweak {
|
||||||
output { outState }
|
output { outState }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +99,7 @@ class ObligationTests {
|
|||||||
// institution is allowed to issue as much cash as they want.
|
// institution is allowed to issue as much cash as they want.
|
||||||
transaction {
|
transaction {
|
||||||
output { outState }
|
output { outState }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Issue() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Issue() }
|
||||||
this `fails with` "output states are issued by a command signer"
|
this `fails with` "output states are issued by a command signer"
|
||||||
}
|
}
|
||||||
transaction {
|
transaction {
|
||||||
@ -106,7 +107,7 @@ class ObligationTests {
|
|||||||
Obligation.State(
|
Obligation.State(
|
||||||
obligor = MINI_CORP,
|
obligor = MINI_CORP,
|
||||||
quantity = 1000.DOLLARS.quantity,
|
quantity = 1000.DOLLARS.quantity,
|
||||||
beneficiary = DUMMY_PUBKEY_1,
|
beneficiary = CHARLIE,
|
||||||
template = megaCorpDollarSettlement
|
template = megaCorpDollarSettlement
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -121,14 +122,14 @@ class ObligationTests {
|
|||||||
// Test generation works.
|
// Test generation works.
|
||||||
val tx = TransactionType.General.Builder(notary = null).apply {
|
val tx = TransactionType.General.Builder(notary = null).apply {
|
||||||
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
||||||
beneficiary = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY)
|
beneficiary = CHARLIE, notary = DUMMY_NOTARY)
|
||||||
signWith(MINI_CORP_KEY)
|
signWith(MINI_CORP_KEY)
|
||||||
}.toSignedTransaction().tx
|
}.toSignedTransaction().tx
|
||||||
assertTrue(tx.inputs.isEmpty())
|
assertTrue(tx.inputs.isEmpty())
|
||||||
val expected = Obligation.State(
|
val expected = Obligation.State(
|
||||||
obligor = MINI_CORP,
|
obligor = MINI_CORP,
|
||||||
quantity = 100.DOLLARS.quantity,
|
quantity = 100.DOLLARS.quantity,
|
||||||
beneficiary = DUMMY_PUBKEY_1,
|
beneficiary = CHARLIE,
|
||||||
template = megaCorpDollarSettlement
|
template = megaCorpDollarSettlement
|
||||||
)
|
)
|
||||||
assertEquals(tx.outputs[0].data, expected)
|
assertEquals(tx.outputs[0].data, expected)
|
||||||
@ -142,7 +143,7 @@ class ObligationTests {
|
|||||||
|
|
||||||
// Move fails: not allowed to summon money.
|
// Move fails: not allowed to summon money.
|
||||||
tweak {
|
tweak {
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +200,7 @@ class ObligationTests {
|
|||||||
// Issue some obligation
|
// Issue some obligation
|
||||||
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
||||||
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
|
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
|
||||||
signWith(MINI_CORP_KEY)
|
signWith(MINI_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
|
||||||
@ -207,16 +208,16 @@ class ObligationTests {
|
|||||||
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
ptx.addInputState(tx.tx.outRef<Obligation.State<Currency>>(0))
|
ptx.addInputState(tx.tx.outRef<Obligation.State<Currency>>(0))
|
||||||
Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
||||||
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
|
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
|
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
|
||||||
@Test
|
@Test
|
||||||
fun `generate close-out net transaction`() {
|
fun `generate close-out net transaction`() {
|
||||||
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)
|
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
|
||||||
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY)
|
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
|
||||||
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
Obligation<Currency>().generateCloseOutNetting(this, ALICE_PUBKEY, obligationAliceToBob, obligationBobToAlice)
|
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
|
||||||
signWith(ALICE_KEY)
|
signWith(ALICE_KEY)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction().tx
|
}.toSignedTransaction().tx
|
||||||
@ -226,24 +227,24 @@ class ObligationTests {
|
|||||||
/** Test generating a transaction to net two obligations of the different sizes, and confirm the balance is correct. */
|
/** Test generating a transaction to net two obligations of the different sizes, and confirm the balance is correct. */
|
||||||
@Test
|
@Test
|
||||||
fun `generate close-out net transaction with remainder`() {
|
fun `generate close-out net transaction with remainder`() {
|
||||||
val obligationAliceToBob = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB_PUBKEY)
|
val obligationAliceToBob = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB)
|
||||||
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY)
|
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
|
||||||
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
Obligation<Currency>().generateCloseOutNetting(this, ALICE_PUBKEY, obligationAliceToBob, obligationBobToAlice)
|
Obligation<Currency>().generateCloseOutNetting(this, ALICE, obligationAliceToBob, obligationBobToAlice)
|
||||||
signWith(ALICE_KEY)
|
signWith(ALICE_KEY)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction().tx
|
}.toSignedTransaction().tx
|
||||||
assertEquals(1, tx.outputs.size)
|
assertEquals(1, tx.outputs.size)
|
||||||
|
|
||||||
val actual = tx.outputs[0].data
|
val actual = tx.outputs[0].data
|
||||||
assertEquals((1000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB_PUBKEY), actual)
|
assertEquals((1000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(ALICE, BOB), actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
|
/** Test generating a transaction to net two obligations of the same size, and therefore there are no outputs. */
|
||||||
@Test
|
@Test
|
||||||
fun `generate payment net transaction`() {
|
fun `generate payment net transaction`() {
|
||||||
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)
|
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
|
||||||
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE_PUBKEY)
|
val obligationBobToAlice = oneMillionDollars.OBLIGATION between Pair(BOB, ALICE)
|
||||||
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val tx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
|
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
|
||||||
signWith(ALICE_KEY)
|
signWith(ALICE_KEY)
|
||||||
@ -256,8 +257,8 @@ class ObligationTests {
|
|||||||
/** Test generating a transaction to two obligations, where one is bigger than the other and therefore there is a remainder. */
|
/** Test generating a transaction to two obligations, where one is bigger than the other and therefore there is a remainder. */
|
||||||
@Test
|
@Test
|
||||||
fun `generate payment net transaction with remainder`() {
|
fun `generate payment net transaction with remainder`() {
|
||||||
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)
|
val obligationAliceToBob = oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)
|
||||||
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE_PUBKEY)
|
val obligationBobToAlice = (2000000.DOLLARS `issued by` defaultIssuer).OBLIGATION between Pair(BOB, ALICE)
|
||||||
val tx = TransactionType.General.Builder(null).apply {
|
val tx = TransactionType.General.Builder(null).apply {
|
||||||
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
|
Obligation<Currency>().generatePaymentNetting(this, obligationAliceToBob.amount.token, DUMMY_NOTARY, obligationAliceToBob, obligationBobToAlice)
|
||||||
signWith(ALICE_KEY)
|
signWith(ALICE_KEY)
|
||||||
@ -278,7 +279,7 @@ class ObligationTests {
|
|||||||
// Generate a transaction issuing the obligation
|
// Generate a transaction issuing the obligation
|
||||||
var tx = TransactionType.General.Builder(null).apply {
|
var tx = TransactionType.General.Builder(null).apply {
|
||||||
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement.copy(dueBefore = dueBefore), 100.DOLLARS.quantity,
|
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement.copy(dueBefore = dueBefore), 100.DOLLARS.quantity,
|
||||||
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
|
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
|
||||||
signWith(MINI_CORP_KEY)
|
signWith(MINI_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
var stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0)
|
var stateAndRef = tx.tx.outRef<Obligation.State<Currency>>(0)
|
||||||
@ -309,14 +310,14 @@ class ObligationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `generate settlement transaction`() {
|
fun `generate settlement transaction`() {
|
||||||
val cashTx = TransactionType.General.Builder(null).apply {
|
val cashTx = TransactionType.General.Builder(null).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP_PUBKEY, DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` defaultIssuer, MINI_CORP, DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
}.toSignedTransaction().tx
|
}.toSignedTransaction().tx
|
||||||
|
|
||||||
// Generate a transaction issuing the obligation
|
// Generate a transaction issuing the obligation
|
||||||
val obligationTx = TransactionType.General.Builder(null).apply {
|
val obligationTx = TransactionType.General.Builder(null).apply {
|
||||||
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
Obligation<Currency>().generateIssue(this, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
||||||
beneficiary = MINI_CORP_PUBKEY, notary = DUMMY_NOTARY)
|
beneficiary = MINI_CORP, notary = DUMMY_NOTARY)
|
||||||
signWith(MINI_CORP_KEY)
|
signWith(MINI_CORP_KEY)
|
||||||
}.toSignedTransaction().tx
|
}.toSignedTransaction().tx
|
||||||
|
|
||||||
@ -354,7 +355,7 @@ class ObligationTests {
|
|||||||
input("Alice's $1,000,000 obligation to Bob")
|
input("Alice's $1,000,000 obligation to Bob")
|
||||||
input("Bob's $1,000,000 obligation to Alice")
|
input("Bob's $1,000,000 obligation to Alice")
|
||||||
input("MegaCorp's $1,000,000 obligation to Bob")
|
input("MegaCorp's $1,000,000 obligation to Bob")
|
||||||
output("change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB_PUBKEY) }
|
output("change") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, BOB) }
|
||||||
command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
|
command(BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -368,7 +369,7 @@ class ObligationTests {
|
|||||||
transaction("Issuance") {
|
transaction("Issuance") {
|
||||||
input("Alice's $1,000,000 obligation to Bob")
|
input("Alice's $1,000,000 obligation to Bob")
|
||||||
input("Bob's $1,000,000 obligation to Alice")
|
input("Bob's $1,000,000 obligation to Alice")
|
||||||
output("change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB_PUBKEY) }
|
output("change") { (oneMillionDollars.splitEvenly(2).first()).OBLIGATION between Pair(ALICE, BOB) }
|
||||||
command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
|
command(BOB_PUBKEY) { Obligation.Commands.Net(NetType.CLOSE_OUT) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this `fails with` "amounts owed on input and output must match"
|
this `fails with` "amounts owed on input and output must match"
|
||||||
@ -422,7 +423,7 @@ class ObligationTests {
|
|||||||
transaction("Issuance") {
|
transaction("Issuance") {
|
||||||
input("Bob's $1,000,000 obligation to Alice")
|
input("Bob's $1,000,000 obligation to Alice")
|
||||||
input("MegaCorp's $1,000,000 obligation to Bob")
|
input("MegaCorp's $1,000,000 obligation to Bob")
|
||||||
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE_PUBKEY) }
|
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) }
|
||||||
command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
|
command(ALICE_PUBKEY, BOB_PUBKEY, MEGA_CORP_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -436,7 +437,7 @@ class ObligationTests {
|
|||||||
transaction("Issuance") {
|
transaction("Issuance") {
|
||||||
input("Bob's $1,000,000 obligation to Alice")
|
input("Bob's $1,000,000 obligation to Alice")
|
||||||
input("MegaCorp's $1,000,000 obligation to Bob")
|
input("MegaCorp's $1,000,000 obligation to Bob")
|
||||||
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE_PUBKEY) }
|
output("MegaCorp's $1,000,000 obligation to Alice") { oneMillionDollars.OBLIGATION between Pair(MEGA_CORP, ALICE) }
|
||||||
command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
|
command(ALICE_PUBKEY, BOB_PUBKEY) { Obligation.Commands.Net(NetType.PAYMENT) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this `fails with` "all involved parties have signed"
|
this `fails with` "all involved parties have signed"
|
||||||
@ -452,7 +453,7 @@ class ObligationTests {
|
|||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input("Alice's $1,000,000 obligation to Bob")
|
input("Alice's $1,000,000 obligation to Bob")
|
||||||
input("Alice's $1,000,000")
|
input("Alice's $1,000,000")
|
||||||
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY }
|
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
|
||||||
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
|
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
|
||||||
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -463,10 +464,10 @@ class ObligationTests {
|
|||||||
val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer
|
val halfAMillionDollars = 500000.DOLLARS `issued by` defaultIssuer
|
||||||
ledger {
|
ledger {
|
||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY))
|
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB))
|
||||||
input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY)
|
input(500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
|
||||||
output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) }
|
output("Alice's $500,000 obligation to Bob") { halfAMillionDollars.OBLIGATION between Pair(ALICE, BOB) }
|
||||||
output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY }
|
output("Bob's $500,000") { 500000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
|
||||||
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
|
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
|
||||||
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -474,12 +475,12 @@ class ObligationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we can't settle an obligation that's defaulted
|
// Make sure we can't settle an obligation that's defaulted
|
||||||
val defaultedObligation: Obligation.State<Currency> = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)).copy(lifecycle = Lifecycle.DEFAULTED)
|
val defaultedObligation: Obligation.State<Currency> = (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED)
|
||||||
ledger {
|
ledger {
|
||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob
|
input(defaultedObligation) // Alice's defaulted $1,000,000 obligation to Bob
|
||||||
input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY)
|
input(1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE)
|
||||||
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY }
|
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
|
||||||
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
|
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity, inState.amount.token)) }
|
||||||
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
||||||
this `fails with` "all inputs are in the normal state"
|
this `fails with` "all inputs are in the normal state"
|
||||||
@ -492,7 +493,7 @@ class ObligationTests {
|
|||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input("Alice's $1,000,000 obligation to Bob")
|
input("Alice's $1,000,000 obligation to Bob")
|
||||||
input("Alice's $1,000,000")
|
input("Alice's $1,000,000")
|
||||||
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB_PUBKEY }
|
output("Bob's $1,000,000") { 1000000.DOLLARS.CASH `issued by` defaultIssuer `owned by` BOB }
|
||||||
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
|
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneMillionDollars.quantity / 2, inState.amount.token)) }
|
||||||
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
command(ALICE_PUBKEY) { Cash.Commands.Move(Obligation<Currency>().legalContractReference) }
|
||||||
this `fails with` "amount in settle command"
|
this `fails with` "amount in settle command"
|
||||||
@ -506,17 +507,17 @@ class ObligationTests {
|
|||||||
val oneUnitFcoj = Amount(1, defaultFcoj)
|
val oneUnitFcoj = Amount(1, defaultFcoj)
|
||||||
val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME)
|
val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME)
|
||||||
val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE,
|
val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE,
|
||||||
obligationDef, oneUnitFcoj.quantity, NullPublicKey)
|
obligationDef, oneUnitFcoj.quantity, NULL_PARTY)
|
||||||
// Try settling a simple commodity obligation
|
// Try settling a simple commodity obligation
|
||||||
ledger {
|
ledger {
|
||||||
unverifiedTransaction {
|
unverifiedTransaction {
|
||||||
output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB_PUBKEY))
|
output("Alice's 1 FCOJ obligation to Bob", oneUnitFcojObligation between Pair(ALICE, BOB))
|
||||||
output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE_PUBKEY))
|
output("Alice's 1 FCOJ", CommodityContract.State(oneUnitFcoj, ALICE))
|
||||||
}
|
}
|
||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input("Alice's 1 FCOJ obligation to Bob")
|
input("Alice's 1 FCOJ obligation to Bob")
|
||||||
input("Alice's 1 FCOJ")
|
input("Alice's 1 FCOJ")
|
||||||
output("Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB_PUBKEY) }
|
output("Bob's 1 FCOJ") { CommodityContract.State(oneUnitFcoj, BOB) }
|
||||||
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) }
|
command(ALICE_PUBKEY) { Obligation.Commands.Settle(Amount(oneUnitFcoj.quantity, oneUnitFcojObligation.amount.token)) }
|
||||||
command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation<Commodity>().legalContractReference) }
|
command(ALICE_PUBKEY) { CommodityContract.Commands.Move(Obligation<Commodity>().legalContractReference) }
|
||||||
verifies()
|
verifies()
|
||||||
@ -531,7 +532,7 @@ class ObligationTests {
|
|||||||
cashObligationTestRoots(this)
|
cashObligationTestRoots(this)
|
||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input("Alice's $1,000,000 obligation to Bob")
|
input("Alice's $1,000,000 obligation to Bob")
|
||||||
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY)).copy(lifecycle = Lifecycle.DEFAULTED) }
|
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB)).copy(lifecycle = Lifecycle.DEFAULTED) }
|
||||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
||||||
this `fails with` "there is a timestamp from the authority"
|
this `fails with` "there is a timestamp from the authority"
|
||||||
}
|
}
|
||||||
@ -541,8 +542,8 @@ class ObligationTests {
|
|||||||
val pastTestTime = TEST_TX_TIME - Duration.ofDays(7)
|
val pastTestTime = TEST_TX_TIME - Duration.ofDays(7)
|
||||||
val futureTestTime = TEST_TX_TIME + Duration.ofDays(7)
|
val futureTestTime = TEST_TX_TIME + Duration.ofDays(7)
|
||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` futureTestTime)
|
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime)
|
||||||
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
||||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this `fails with` "the due date has passed"
|
this `fails with` "the due date has passed"
|
||||||
@ -551,8 +552,8 @@ class ObligationTests {
|
|||||||
// Try defaulting an obligation that is now in the past
|
// Try defaulting an obligation that is now in the past
|
||||||
ledger {
|
ledger {
|
||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` pastTestTime)
|
input(oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime)
|
||||||
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB_PUBKEY) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION between Pair(ALICE, BOB) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
||||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Lifecycle.DEFAULTED) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
this.verifies()
|
this.verifies()
|
||||||
@ -565,7 +566,7 @@ class ObligationTests {
|
|||||||
fun testMergeSplit() {
|
fun testMergeSplit() {
|
||||||
// Splitting value works.
|
// Splitting value works.
|
||||||
transaction {
|
transaction {
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
tweak {
|
tweak {
|
||||||
input { inState }
|
input { inState }
|
||||||
repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } }
|
repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } }
|
||||||
@ -624,7 +625,7 @@ class ObligationTests {
|
|||||||
inState.copy(
|
inState.copy(
|
||||||
quantity = 15000,
|
quantity = 15000,
|
||||||
template = megaCorpPoundSettlement,
|
template = megaCorpPoundSettlement,
|
||||||
beneficiary = DUMMY_PUBKEY_2
|
beneficiary = AnonymousParty(DUMMY_PUBKEY_2)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
output { outState.copy(quantity = 115000) }
|
output { outState.copy(quantity = 115000) }
|
||||||
@ -635,7 +636,7 @@ class ObligationTests {
|
|||||||
input { inState }
|
input { inState }
|
||||||
input { inState `issued by` MINI_CORP }
|
input { inState `issued by` MINI_CORP }
|
||||||
output { outState }
|
output { outState }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -648,17 +649,17 @@ class ObligationTests {
|
|||||||
output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
|
output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) }
|
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(100.DOLLARS.quantity, inState.amount.token)) }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token)) }
|
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token)) }
|
||||||
this `fails with` "required net.corda.core.contracts.FungibleAsset.Commands.Move command"
|
this `fails with` "required net.corda.core.contracts.FungibleAsset.Commands.Move command"
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -676,14 +677,14 @@ class ObligationTests {
|
|||||||
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) }
|
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedPounds), quantity = inState.quantity - 200.POUNDS.quantity) }
|
||||||
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) }
|
output { inState.copy(template = inState.template.copy(acceptableIssuedProducts = megaIssuedDollars), quantity = inState.quantity - 200.DOLLARS.quantity) }
|
||||||
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
|
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement))) }
|
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.DOLLARS.quantity, inState.amount.token.copy(product = megaCorpDollarSettlement))) }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement))) }
|
command(CHARLIE.owningKey) { Obligation.Commands.Exit(Amount(200.POUNDS.quantity, inState.amount.token.copy(product = megaCorpPoundSettlement))) }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -697,20 +698,20 @@ class ObligationTests {
|
|||||||
|
|
||||||
// Can't merge them together.
|
// Can't merge them together.
|
||||||
tweak {
|
tweak {
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2, quantity = 200000L) }
|
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2), quantity = 200000L) }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
// Missing MiniCorp deposit
|
// Missing MiniCorp deposit
|
||||||
tweak {
|
tweak {
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
|
|
||||||
// This works.
|
// This works.
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) }
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) `issued by` MINI_CORP }
|
output { inState.copy(beneficiary = AnonymousParty(DUMMY_PUBKEY_2)) `issued by` MINI_CORP }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey) { Obligation.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -719,12 +720,12 @@ class ObligationTests {
|
|||||||
fun multiCurrency() {
|
fun multiCurrency() {
|
||||||
// Check we can do an atomic currency trade tx.
|
// Check we can do an atomic currency trade tx.
|
||||||
transaction {
|
transaction {
|
||||||
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, DUMMY_PUBKEY_2)
|
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, AnonymousParty(DUMMY_PUBKEY_2))
|
||||||
input { inState `owned by` DUMMY_PUBKEY_1 }
|
input { inState `owned by` CHARLIE }
|
||||||
input { pounds }
|
input { pounds }
|
||||||
output { inState `owned by` DUMMY_PUBKEY_2 }
|
output { inState `owned by` AnonymousParty(DUMMY_PUBKEY_2) }
|
||||||
output { pounds `owned by` DUMMY_PUBKEY_1 }
|
output { pounds `owned by` CHARLIE }
|
||||||
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Obligation.Commands.Move() }
|
command(CHARLIE.owningKey, DUMMY_PUBKEY_2) { Obligation.Commands.Move() }
|
||||||
|
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
@ -733,11 +734,11 @@ class ObligationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `nettability of settlement contracts`() {
|
fun `nettability of settlement contracts`() {
|
||||||
val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
||||||
5000.DOLLARS.quantity, MEGA_CORP_PUBKEY)
|
5000.DOLLARS.quantity, MEGA_CORP)
|
||||||
val twoKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
val twoKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
||||||
2000.DOLLARS.quantity, MINI_CORP_PUBKEY)
|
2000.DOLLARS.quantity, MINI_CORP)
|
||||||
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
|
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
|
||||||
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY)
|
1000.DOLLARS.quantity, MEGA_CORP)
|
||||||
|
|
||||||
// Obviously states must be nettable with themselves
|
// Obviously states must be nettable with themselves
|
||||||
assertEquals(fiveKDollarsFromMegaToMega.bilateralNetState, fiveKDollarsFromMegaToMega.bilateralNetState)
|
assertEquals(fiveKDollarsFromMegaToMega.bilateralNetState, fiveKDollarsFromMegaToMega.bilateralNetState)
|
||||||
@ -778,9 +779,9 @@ class ObligationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `extraction of issuance defintion`() {
|
fun `extraction of issuance defintion`() {
|
||||||
val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
val fiveKDollarsFromMegaToMega = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
||||||
5000.DOLLARS.quantity, MEGA_CORP_PUBKEY)
|
5000.DOLLARS.quantity, MEGA_CORP)
|
||||||
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
|
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
|
||||||
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY)
|
1000.DOLLARS.quantity, MEGA_CORP)
|
||||||
|
|
||||||
// Issuance definitions must match the input
|
// Issuance definitions must match the input
|
||||||
assertEquals(fiveKDollarsFromMegaToMega.template, megaCorpDollarSettlement)
|
assertEquals(fiveKDollarsFromMegaToMega.template, megaCorpDollarSettlement)
|
||||||
@ -791,14 +792,14 @@ class ObligationTests {
|
|||||||
fun `adding two settlement contracts nets them`() {
|
fun `adding two settlement contracts nets them`() {
|
||||||
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
|
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
|
||||||
val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
||||||
5000.DOLLARS.quantity, MINI_CORP_PUBKEY)
|
5000.DOLLARS.quantity, MINI_CORP)
|
||||||
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
|
val oneKDollarsFromMiniToMega = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement,
|
||||||
1000.DOLLARS.quantity, MEGA_CORP_PUBKEY)
|
1000.DOLLARS.quantity, MEGA_CORP)
|
||||||
|
|
||||||
var actual = fiveKDollarsFromMegaToMini.net(fiveKDollarsFromMegaToMini.copy(quantity = 2000.DOLLARS.quantity))
|
var actual = fiveKDollarsFromMegaToMini.net(fiveKDollarsFromMegaToMini.copy(quantity = 2000.DOLLARS.quantity))
|
||||||
// Both pay from mega to mini, so we add directly
|
// Both pay from mega to mini, so we add directly
|
||||||
var expected = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, 7000.DOLLARS.quantity,
|
var expected = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, 7000.DOLLARS.quantity,
|
||||||
MINI_CORP_PUBKEY)
|
MINI_CORP)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
|
|
||||||
// Reversing the direction should mean adding the second state subtracts from the first
|
// Reversing the direction should mean adding the second state subtracts from the first
|
||||||
@ -809,7 +810,7 @@ class ObligationTests {
|
|||||||
// Trying to add an incompatible state must throw an error
|
// Trying to add an incompatible state must throw an error
|
||||||
assertFailsWith(IllegalArgumentException::class) {
|
assertFailsWith(IllegalArgumentException::class) {
|
||||||
fiveKDollarsFromMegaToMini.net(Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement, 1000.DOLLARS.quantity,
|
fiveKDollarsFromMegaToMini.net(Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpDollarSettlement, 1000.DOLLARS.quantity,
|
||||||
MINI_CORP_PUBKEY))
|
MINI_CORP))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,9 +818,9 @@ class ObligationTests {
|
|||||||
fun `extracting amounts due between parties from a list of states`() {
|
fun `extracting amounts due between parties from a list of states`() {
|
||||||
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
|
val megaCorpDollarSettlement = Obligation.Terms(trustedCashContract, megaIssuedDollars, fivePm)
|
||||||
val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
|
||||||
5000.DOLLARS.quantity, MINI_CORP_PUBKEY)
|
5000.DOLLARS.quantity, MINI_CORP)
|
||||||
val amount = fiveKDollarsFromMegaToMini.amount
|
val amount = fiveKDollarsFromMegaToMini.amount
|
||||||
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<Currency>>> = mapOf(Pair(Pair(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Amount(amount.quantity, amount.token.product)))
|
val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Obligation.Terms<Currency>>> = mapOf(Pair(Pair(MEGA_CORP, MINI_CORP), Amount(amount.quantity, amount.token.product)))
|
||||||
val actual = extractAmountsDue(megaCorpDollarSettlement, listOf(fiveKDollarsFromMegaToMini))
|
val actual = extractAmountsDue(megaCorpDollarSettlement, listOf(fiveKDollarsFromMegaToMini))
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -827,24 +828,24 @@ class ObligationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `netting equal balances due between parties`() {
|
fun `netting equal balances due between parties`() {
|
||||||
// Now try it with two balances, which cancel each other out
|
// Now try it with two balances, which cancel each other out
|
||||||
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
|
val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
|
||||||
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
|
Pair(Pair(ALICE, BOB), Amount(100000000, GBP)),
|
||||||
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
|
Pair(Pair(BOB, ALICE), Amount(100000000, GBP))
|
||||||
)
|
)
|
||||||
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
|
val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
|
||||||
val actual: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = netAmountsDue(balanced)
|
val actual: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = netAmountsDue(balanced)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `netting difference balances due between parties`() {
|
fun `netting difference balances due between parties`() {
|
||||||
// Now try it with two balances, which cancel each other out
|
// Now try it with two balances, which cancel each other out
|
||||||
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
|
val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
|
||||||
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
|
Pair(Pair(ALICE, BOB), Amount(100000000, GBP)),
|
||||||
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(200000000, GBP))
|
Pair(Pair(BOB, ALICE), Amount(200000000, GBP))
|
||||||
)
|
)
|
||||||
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
|
val expected: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
|
||||||
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
|
Pair(Pair(BOB, ALICE), Amount(100000000, GBP))
|
||||||
)
|
)
|
||||||
val actual = netAmountsDue(balanced)
|
val actual = netAmountsDue(balanced)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -852,16 +853,16 @@ class ObligationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `summing empty balances due between parties`() {
|
fun `summing empty balances due between parties`() {
|
||||||
val empty = emptyMap<Pair<PublicKey, PublicKey>, Amount<Currency>>()
|
val empty = emptyMap<Pair<AbstractParty, AbstractParty>, Amount<Currency>>()
|
||||||
val expected = emptyMap<PublicKey, Long>()
|
val expected = emptyMap<AbstractParty, Long>()
|
||||||
val actual = sumAmountsDue(empty)
|
val actual = sumAmountsDue(empty)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `summing balances due between parties`() {
|
fun `summing balances due between parties`() {
|
||||||
val simple: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)))
|
val simple: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(Pair(Pair(ALICE, BOB), Amount(100000000, GBP)))
|
||||||
val expected: Map<PublicKey, Long> = mapOf(Pair(ALICE_PUBKEY, -100000000L), Pair(BOB_PUBKEY, 100000000L))
|
val expected: Map<AbstractParty, Long> = mapOf(Pair(ALICE, -100000000L), Pair(BOB, 100000000L))
|
||||||
val actual = sumAmountsDue(simple)
|
val actual = sumAmountsDue(simple)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -869,11 +870,11 @@ class ObligationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `summing balances due between parties which net to zero`() {
|
fun `summing balances due between parties which net to zero`() {
|
||||||
// Now try it with two balances, which cancel each other out
|
// Now try it with two balances, which cancel each other out
|
||||||
val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
|
val balanced: Map<Pair<AbstractParty, AbstractParty>, Amount<Currency>> = mapOf(
|
||||||
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
|
Pair(Pair(ALICE, BOB), Amount(100000000, GBP)),
|
||||||
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
|
Pair(Pair(BOB, ALICE), Amount(100000000, GBP))
|
||||||
)
|
)
|
||||||
val expected: Map<PublicKey, Long> = emptyMap() // Zero balances are stripped before returning
|
val expected: Map<AbstractParty, Long> = emptyMap() // Zero balances are stripped before returning
|
||||||
val actual = sumAmountsDue(balanced)
|
val actual = sumAmountsDue(balanced)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import net.corda.core.contracts.CommandData
|
|||||||
import net.corda.core.contracts.ContractState
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.TransactionType
|
import net.corda.core.contracts.TransactionType
|
||||||
import net.corda.core.crypto.NullSignature
|
import net.corda.core.crypto.NullSignature
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.testing.*
|
import net.corda.core.testing.*
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
@ -25,7 +26,7 @@ class ContractStateGenerator : Generator<ContractState>(ContractState::class.jav
|
|||||||
override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState {
|
override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState {
|
||||||
return Cash.State(
|
return Cash.State(
|
||||||
amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status),
|
amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status),
|
||||||
owner = PublicKeyGenerator().generate(random, status)
|
owner = AnonymousParty(PublicKeyGenerator().generate(random, status))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,8 @@ class CashPaymentFlowTests {
|
|||||||
net.runNetwork()
|
net.runNetwork()
|
||||||
val paymentTx = future.getOrThrow()
|
val paymentTx = future.getOrThrow()
|
||||||
val states = paymentTx.tx.outputs.map { it.data }.filterIsInstance<Cash.State>()
|
val states = paymentTx.tx.outputs.map { it.data }.filterIsInstance<Cash.State>()
|
||||||
val ourState = states.single { it.owner != payTo.owningKey }
|
val ourState = states.single { it.owner.owningKey != payTo.owningKey }
|
||||||
val paymentState = states.single { it.owner == payTo.owningKey }
|
val paymentState = states.single { it.owner.owningKey == payTo.owningKey }
|
||||||
assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), paymentState.amount)
|
assertEquals(expected.`issued by`(bankOfCorda.ref(ref)), paymentState.amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,11 @@ import io.requery.rx.KotlinRxEntityStore
|
|||||||
import io.requery.sql.*
|
import io.requery.sql.*
|
||||||
import io.requery.sql.platform.Generic
|
import io.requery.sql.platform.Generic
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.CompositeKey
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.generateKeyPair
|
||||||
|
import net.corda.core.crypto.toBase58String
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.schemas.requery.converters.InstantConverter
|
import net.corda.core.schemas.requery.converters.InstantConverter
|
||||||
@ -19,17 +23,16 @@ import net.corda.core.schemas.requery.converters.VaultStateStatusConverter
|
|||||||
import net.corda.core.serialization.deserialize
|
import net.corda.core.serialization.deserialize
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
|
import net.corda.core.utilities.ALICE
|
||||||
|
import net.corda.core.utilities.BOB
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_1
|
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_2
|
|
||||||
import org.h2.jdbcx.JdbcDataSource
|
import org.h2.jdbcx.JdbcDataSource
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
@ -81,12 +84,12 @@ class VaultSchemaTest {
|
|||||||
private class VaultNoopContract : Contract {
|
private class VaultNoopContract : Contract {
|
||||||
override val legalContractReference = SecureHash.sha256("")
|
override val legalContractReference = SecureHash.sha256("")
|
||||||
|
|
||||||
data class VaultNoopState(override val owner: PublicKey) : OwnableState {
|
data class VaultNoopState(override val owner: AbstractParty) : OwnableState {
|
||||||
override val contract = VaultNoopContract()
|
override val contract = VaultNoopContract()
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Create(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: AbstractParty) = Pair(Commands.Create(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
@ -101,10 +104,10 @@ class VaultSchemaTest {
|
|||||||
private fun setupDummyData() {
|
private fun setupDummyData() {
|
||||||
// dummy Transaction
|
// dummy Transaction
|
||||||
val notary: Party = DUMMY_NOTARY
|
val notary: Party = DUMMY_NOTARY
|
||||||
val inState1 = TransactionState(DummyContract.SingleOwnerState(0, DUMMY_PUBKEY_1), notary)
|
val inState1 = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary)
|
||||||
val inState2 = TransactionState(DummyContract.MultiOwnerState(0,
|
val inState2 = TransactionState(DummyContract.MultiOwnerState(0,
|
||||||
listOf(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2)), notary)
|
listOf(ALICE, BOB)), notary)
|
||||||
val inState3 = TransactionState(VaultNoopContract.VaultNoopState(DUMMY_PUBKEY_1), notary)
|
val inState3 = TransactionState(VaultNoopContract.VaultNoopState(ALICE), notary)
|
||||||
val outState1 = inState1.copy()
|
val outState1 = inState1.copy()
|
||||||
val outState2 = inState2.copy()
|
val outState2 = inState2.copy()
|
||||||
val outState3 = inState3.copy()
|
val outState3 = inState3.copy()
|
||||||
@ -132,9 +135,9 @@ class VaultSchemaTest {
|
|||||||
|
|
||||||
private fun createTxnWithTwoStateTypes(): LedgerTransaction {
|
private fun createTxnWithTwoStateTypes(): LedgerTransaction {
|
||||||
val notary: Party = DUMMY_NOTARY
|
val notary: Party = DUMMY_NOTARY
|
||||||
val inState1 = TransactionState(DummyContract.SingleOwnerState(0, DUMMY_PUBKEY_1), notary)
|
val inState1 = TransactionState(DummyContract.SingleOwnerState(0, ALICE), notary)
|
||||||
val inState2 = TransactionState(DummyContract.MultiOwnerState(0,
|
val inState2 = TransactionState(DummyContract.MultiOwnerState(0,
|
||||||
listOf(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2)), notary)
|
listOf(ALICE, BOB)), notary)
|
||||||
val outState1 = inState1.copy()
|
val outState1 = inState1.copy()
|
||||||
val outState2 = inState2.copy()
|
val outState2 = inState2.copy()
|
||||||
val state1TxHash = SecureHash.randomSHA256()
|
val state1TxHash = SecureHash.randomSHA256()
|
||||||
|
@ -57,7 +57,7 @@ class BFTNotaryServiceTests : NodeBasedTest() {
|
|||||||
firstSpend.resultFuture.getOrThrow()
|
firstSpend.resultFuture.getOrThrow()
|
||||||
|
|
||||||
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
|
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
|
||||||
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity.owningKey)
|
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity)
|
||||||
addOutputState(dummyState)
|
addOutputState(dummyState)
|
||||||
signWith(aliceKey)
|
signWith(aliceKey)
|
||||||
toSignedTransaction(false)
|
toSignedTransaction(false)
|
||||||
|
@ -46,7 +46,7 @@ class RaftNotaryServiceTests : NodeBasedTest() {
|
|||||||
firstSpend.resultFuture.getOrThrow()
|
firstSpend.resultFuture.getOrThrow()
|
||||||
|
|
||||||
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
|
val secondSpendTx = TransactionType.General.Builder(notaryParty).withItems(inputState).run {
|
||||||
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity.owningKey)
|
val dummyState = DummyContract.SingleOwnerState(0, alice.info.legalIdentity)
|
||||||
addOutputState(dummyState)
|
addOutputState(dummyState)
|
||||||
signWith(aliceKey)
|
signWith(aliceKey)
|
||||||
toSignedTransaction(false)
|
toSignedTransaction(false)
|
||||||
|
@ -115,7 +115,7 @@ class ContractUpgradeHandler(otherSide: Party) : AbstractStateReplacementFlow.Ac
|
|||||||
val proposedTx = proposal.stx.tx
|
val proposedTx = proposal.stx.tx
|
||||||
val expectedTx = ContractUpgradeFlow.assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction()
|
val expectedTx = ContractUpgradeFlow.assembleBareTx(oldStateAndRef, proposal.modification).toWireTransaction()
|
||||||
requireThat {
|
requireThat {
|
||||||
"The instigator is one of the participants" using (otherSide.owningKey in oldStateAndRef.state.data.participants)
|
"The instigator is one of the participants" using (otherSide in oldStateAndRef.state.data.participants)
|
||||||
"The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification == authorisedUpgrade)
|
"The proposed upgrade ${proposal.modification.javaClass} is a trusted upgrade path" using (proposal.modification == authorisedUpgrade)
|
||||||
"The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx)
|
"The proposed tx matches the expected tx for this upgrade" using (proposedTx == expectedTx)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.node.services.identity
|
|||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.contracts.PartyAndReference
|
||||||
import net.corda.core.contracts.requireThat
|
import net.corda.core.contracts.requireThat
|
||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
@ -16,7 +17,6 @@ import java.security.cert.*
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
import javax.security.auth.x500.X500Principal
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple identity service which caches parties and provides functionality for efficient lookup.
|
* Simple identity service which caches parties and provides functionality for efficient lookup.
|
||||||
@ -44,7 +44,7 @@ class InMemoryIdentityService : SingletonSerializeAsToken(), IdentityService {
|
|||||||
@Deprecated("Use partyFromX500Name")
|
@Deprecated("Use partyFromX500Name")
|
||||||
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
|
override fun partyFromName(name: String): Party? = principalToParties[X500Name(name)]
|
||||||
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]
|
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]
|
||||||
override fun partyFromAnonymous(party: AnonymousParty): Party? = partyFromKey(party.owningKey)
|
override fun partyFromAnonymous(party: AbstractParty): Party? = partyFromKey(party.owningKey)
|
||||||
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
|
||||||
|
|
||||||
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
||||||
|
@ -11,11 +11,12 @@ import io.requery.kotlin.notNull
|
|||||||
import io.requery.query.RowExpression
|
import io.requery.query.RowExpression
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.contracts.asset.OnLedgerAsset
|
import net.corda.contracts.asset.OnLedgerAsset
|
||||||
import net.corda.contracts.clause.AbstractConserveAmount
|
|
||||||
import net.corda.core.ThreadBox
|
import net.corda.core.ThreadBox
|
||||||
import net.corda.core.bufferUntilSubscribed
|
import net.corda.core.bufferUntilSubscribed
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.containsAny
|
||||||
|
import net.corda.core.crypto.toBase58String
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
@ -468,7 +469,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
override fun generateSpend(tx: TransactionBuilder,
|
override fun generateSpend(tx: TransactionBuilder,
|
||||||
amount: Amount<Currency>,
|
amount: Amount<Currency>,
|
||||||
to: PublicKey,
|
to: AbstractParty,
|
||||||
onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<PublicKey>> {
|
onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<PublicKey>> {
|
||||||
// Retrieve unspent and unlocked cash states that meet our spending criteria.
|
// Retrieve unspent and unlocked cash states that meet our spending criteria.
|
||||||
val acceptableCoins = unconsumedStatesForSpending<Cash.State>(amount, onlyFromParties, tx.notary, tx.lockId)
|
val acceptableCoins = unconsumedStatesForSpending<Cash.State>(amount, onlyFromParties, tx.notary, tx.lockId)
|
||||||
@ -477,7 +478,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
|||||||
{ Cash().generateMoveCommand() })
|
{ Cash().generateMoveCommand() })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
|
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: AbstractParty)
|
||||||
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
||||||
|
|
||||||
private fun makeUpdate(tx: WireTransaction, ourKeys: Set<PublicKey>): Vault.Update {
|
private fun makeUpdate(tx: WireTransaction, ourKeys: Set<PublicKey>): Vault.Update {
|
||||||
@ -528,7 +529,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>) = when (state) {
|
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>) = when (state) {
|
||||||
is OwnableState -> state.owner.containsAny(ourKeys)
|
is OwnableState -> state.owner.owningKey.containsAny(ourKeys)
|
||||||
// It's potentially of interest to the vault
|
// It's potentially of interest to the vault
|
||||||
is LinearState -> state.isRelevant(ourKeys)
|
is LinearState -> state.isRelevant(ourKeys)
|
||||||
else -> false
|
else -> false
|
||||||
|
@ -91,7 +91,7 @@ class CordaRPCOpsImplTest {
|
|||||||
|
|
||||||
val expectedState = Cash.State(Amount(quantity,
|
val expectedState = Cash.State(Amount(quantity,
|
||||||
Issued(aliceNode.info.legalIdentity.ref(ref), GBP)),
|
Issued(aliceNode.info.legalIdentity.ref(ref), GBP)),
|
||||||
recipient.owningKey)
|
recipient)
|
||||||
|
|
||||||
var issueSmId: StateMachineRunId? = null
|
var issueSmId: StateMachineRunId? = null
|
||||||
stateMachineUpdates.expectEvents {
|
stateMachineUpdates.expectEvents {
|
||||||
|
@ -14,6 +14,7 @@ import net.corda.core.flows.FlowStateMachine
|
|||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
import net.corda.core.flows.StateMachineRunId
|
import net.corda.core.flows.StateMachineRunId
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.map
|
import net.corda.core.map
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
@ -97,7 +98,7 @@ class TwoPartyTradeFlowTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val alicesFakePaper = aliceNode.database.transaction {
|
val alicesFakePaper = aliceNode.database.transaction {
|
||||||
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +145,7 @@ class TwoPartyTradeFlowTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val alicesFakePaper = aliceNode.database.transaction {
|
val alicesFakePaper = aliceNode.database.transaction {
|
||||||
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +196,7 @@ class TwoPartyTradeFlowTests {
|
|||||||
bobNode.services.fillWithSomeTestCash(2000.DOLLARS, outputNotary = notaryNode.info.notaryIdentity)
|
bobNode.services.fillWithSomeTestCash(2000.DOLLARS, outputNotary = notaryNode.info.notaryIdentity)
|
||||||
}
|
}
|
||||||
val alicesFakePaper = aliceNode.database.transaction {
|
val alicesFakePaper = aliceNode.database.transaction {
|
||||||
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, null, notaryNode.info.notaryIdentity).second
|
||||||
}
|
}
|
||||||
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey)
|
insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey, notaryKey)
|
||||||
@ -301,6 +302,7 @@ class TwoPartyTradeFlowTests {
|
|||||||
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
|
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||||
val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name)
|
val aliceNode = makeNodeWithTracking(notaryNode.info.address, ALICE.name)
|
||||||
val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name)
|
val bobNode = makeNodeWithTracking(notaryNode.info.address, BOB.name)
|
||||||
|
val alice = aliceNode.info.legalIdentity
|
||||||
val aliceKey = aliceNode.services.legalIdentityKey
|
val aliceKey = aliceNode.services.legalIdentityKey
|
||||||
|
|
||||||
ledger(aliceNode.services) {
|
ledger(aliceNode.services) {
|
||||||
@ -317,12 +319,13 @@ class TwoPartyTradeFlowTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val extraKey = bobNode.keyManagement.freshKey()
|
val extraKey = bobNode.keyManagement.freshKey()
|
||||||
val bobsFakeCash = fillUpForBuyer(false, extraKey.public,
|
val extraPublicKey = extraKey.public
|
||||||
|
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(extraPublicKey),
|
||||||
DUMMY_CASH_ISSUER.party,
|
DUMMY_CASH_ISSUER.party,
|
||||||
notaryNode.info.notaryIdentity).second
|
notaryNode.info.notaryIdentity).second
|
||||||
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey)
|
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey)
|
||||||
val alicesFakePaper = aliceNode.database.transaction {
|
val alicesFakePaper = aliceNode.database.transaction {
|
||||||
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
fillUpForSeller(false, alice,
|
||||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
||||||
}
|
}
|
||||||
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey)
|
val alicesSignedTxns = insertFakeTransactions(alicesFakePaper, aliceNode, notaryNode, aliceKey)
|
||||||
@ -416,13 +419,14 @@ class TwoPartyTradeFlowTests {
|
|||||||
attachment(ByteArrayInputStream(stream.toByteArray()))
|
attachment(ByteArrayInputStream(stream.toByteArray()))
|
||||||
}
|
}
|
||||||
|
|
||||||
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public,
|
val bobsKey = bobNode.keyManagement.freshKey().public
|
||||||
|
val bobsFakeCash = fillUpForBuyer(false, AnonymousParty(bobsKey),
|
||||||
DUMMY_CASH_ISSUER.party,
|
DUMMY_CASH_ISSUER.party,
|
||||||
notaryNode.info.notaryIdentity).second
|
notaryNode.info.notaryIdentity).second
|
||||||
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode)
|
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode)
|
||||||
|
|
||||||
val alicesFakePaper = aliceNode.database.transaction {
|
val alicesFakePaper = aliceNode.database.transaction {
|
||||||
fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
fillUpForSeller(false, aliceNode.info.legalIdentity,
|
||||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,14 +524,16 @@ class TwoPartyTradeFlowTests {
|
|||||||
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
|
val notaryNode = net.createNotaryNode(null, DUMMY_NOTARY.name)
|
||||||
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
|
val aliceNode = net.createPartyNode(notaryNode.info.address, ALICE.name)
|
||||||
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
|
val bobNode = net.createPartyNode(notaryNode.info.address, BOB.name)
|
||||||
|
val alice = aliceNode.info.legalIdentity
|
||||||
val aliceKey = aliceNode.services.legalIdentityKey
|
val aliceKey = aliceNode.services.legalIdentityKey
|
||||||
|
val bob = bobNode.info.legalIdentity
|
||||||
val bobKey = bobNode.services.legalIdentityKey
|
val bobKey = bobNode.services.legalIdentityKey
|
||||||
val issuer = MEGA_CORP.ref(1, 2, 3)
|
val issuer = MEGA_CORP.ref(1, 2, 3)
|
||||||
|
|
||||||
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public, DUMMY_CASH_ISSUER.party,
|
val bobsBadCash = fillUpForBuyer(bobError, bob, DUMMY_CASH_ISSUER.party,
|
||||||
notaryNode.info.notaryIdentity).second
|
notaryNode.info.notaryIdentity).second
|
||||||
val alicesFakePaper = aliceNode.database.transaction {
|
val alicesFakePaper = aliceNode.database.transaction {
|
||||||
fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey,
|
fillUpForSeller(aliceError, alice,
|
||||||
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,16 +577,16 @@ class TwoPartyTradeFlowTests {
|
|||||||
|
|
||||||
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
|
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
|
||||||
withError: Boolean,
|
withError: Boolean,
|
||||||
owner: PublicKey,
|
owner: AbstractParty,
|
||||||
issuer: AnonymousParty,
|
issuer: AbstractParty,
|
||||||
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
|
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
|
||||||
val interimOwnerKey = MEGA_CORP_PUBKEY
|
val interimOwner = MEGA_CORP
|
||||||
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
||||||
// wants to sell to Bob.
|
// wants to sell to Bob.
|
||||||
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
val eb1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||||
// Issued money to itself.
|
// Issued money to itself.
|
||||||
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwnerKey }
|
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
|
||||||
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` interimOwnerKey }
|
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` interimOwner }
|
||||||
if (!withError) {
|
if (!withError) {
|
||||||
command(issuer.owningKey) { Cash.Commands.Issue() }
|
command(issuer.owningKey) { Cash.Commands.Issue() }
|
||||||
} else {
|
} else {
|
||||||
@ -599,15 +605,15 @@ class TwoPartyTradeFlowTests {
|
|||||||
val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
val bc1 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||||
input("elbonian money 1")
|
input("elbonian money 1")
|
||||||
output("bob cash 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
output("bob cash 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
||||||
command(interimOwnerKey) { Cash.Commands.Move() }
|
command(interimOwner.owningKey) { Cash.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
|
|
||||||
val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
val bc2 = transaction(transactionBuilder = TransactionBuilder(notary = notary)) {
|
||||||
input("elbonian money 2")
|
input("elbonian money 2")
|
||||||
output("bob cash 2", notary = notary) { 300.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
output("bob cash 2", notary = notary) { 300.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
||||||
output(notary = notary) { 700.DOLLARS.CASH `issued by` issuer `owned by` interimOwnerKey } // Change output.
|
output(notary = notary) { 700.DOLLARS.CASH `issued by` issuer `owned by` interimOwner } // Change output.
|
||||||
command(interimOwnerKey) { Cash.Commands.Move() }
|
command(interimOwner.owningKey) { Cash.Commands.Move() }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,7 +623,7 @@ class TwoPartyTradeFlowTests {
|
|||||||
|
|
||||||
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
|
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
|
||||||
withError: Boolean,
|
withError: Boolean,
|
||||||
owner: PublicKey,
|
owner: AbstractParty,
|
||||||
amount: Amount<Issued<Currency>>,
|
amount: Amount<Issued<Currency>>,
|
||||||
attachmentID: SecureHash?,
|
attachmentID: SecureHash?,
|
||||||
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
|
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
|
||||||
|
@ -123,9 +123,9 @@ class NotaryChangeTests {
|
|||||||
val owner = node.info.legalIdentity.ref(0)
|
val owner = node.info.legalIdentity.ref(0)
|
||||||
val notary = notaryNode.info.notaryIdentity
|
val notary = notaryNode.info.notaryIdentity
|
||||||
|
|
||||||
val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey)
|
val stateA = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
|
||||||
val stateB = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey)
|
val stateB = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
|
||||||
val stateC = DummyContract.SingleOwnerState(Random().nextInt(), owner.party.owningKey)
|
val stateC = DummyContract.SingleOwnerState(Random().nextInt(), owner.party)
|
||||||
|
|
||||||
val tx = TransactionType.General.Builder(null).apply {
|
val tx = TransactionType.General.Builder(null).apply {
|
||||||
addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey))
|
addCommand(Command(DummyContract.Commands.Create(), owner.party.owningKey))
|
||||||
@ -162,7 +162,7 @@ fun issueState(node: AbstractNode, notaryNode: AbstractNode): StateAndRef<*> {
|
|||||||
|
|
||||||
fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> {
|
fun issueMultiPartyState(nodeA: AbstractNode, nodeB: AbstractNode, notaryNode: AbstractNode): StateAndRef<DummyContract.MultiOwnerState> {
|
||||||
val state = TransactionState(DummyContract.MultiOwnerState(0,
|
val state = TransactionState(DummyContract.MultiOwnerState(0,
|
||||||
listOf(nodeA.info.legalIdentity.owningKey, nodeB.info.legalIdentity.owningKey)), notaryNode.info.notaryIdentity)
|
listOf(nodeA.info.legalIdentity, nodeB.info.legalIdentity)), notaryNode.info.notaryIdentity)
|
||||||
val tx = TransactionType.NotaryChange.Builder(notaryNode.info.notaryIdentity).withItems(state)
|
val tx = TransactionType.NotaryChange.Builder(notaryNode.info.notaryIdentity).withItems(state)
|
||||||
val nodeAKey = nodeA.services.legalIdentityKey
|
val nodeAKey = nodeA.services.legalIdentityKey
|
||||||
val nodeBKey = nodeB.services.legalIdentityKey
|
val nodeBKey = nodeB.services.legalIdentityKey
|
||||||
|
@ -10,6 +10,7 @@ import net.corda.core.crypto.DigitalSignature
|
|||||||
import net.corda.core.crypto.NullPublicKey
|
import net.corda.core.crypto.NullPublicKey
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toBase58String
|
import net.corda.core.crypto.toBase58String
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.serialization.storageKryo
|
import net.corda.core.serialization.storageKryo
|
||||||
@ -129,7 +130,7 @@ class RequeryConfigurationTest {
|
|||||||
index = txnState.index
|
index = txnState.index
|
||||||
stateStatus = Vault.StateStatus.UNCONSUMED
|
stateStatus = Vault.StateStatus.UNCONSUMED
|
||||||
contractStateClassName = DummyContract.SingleOwnerState::class.java.name
|
contractStateClassName = DummyContract.SingleOwnerState::class.java.name
|
||||||
contractState = DummyContract.SingleOwnerState(owner = DUMMY_PUBKEY_1).serialize(storageKryo()).bytes
|
contractState = DummyContract.SingleOwnerState(owner = AnonymousParty(DUMMY_PUBKEY_1)).serialize(storageKryo()).bytes
|
||||||
notaryName = txn.tx.notary!!.name.toString()
|
notaryName = txn.tx.notary!!.name.toString()
|
||||||
notaryKey = txn.tx.notary!!.owningKey.toBase58String()
|
notaryKey = txn.tx.notary!!.owningKey.toBase58String()
|
||||||
recordedTime = Instant.now()
|
recordedTime = Instant.now()
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.core.days
|
|||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.FlowLogicRef
|
import net.corda.core.flows.FlowLogicRef
|
||||||
import net.corda.core.flows.FlowLogicRefFactory
|
import net.corda.core.flows.FlowLogicRefFactory
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
@ -113,7 +114,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState {
|
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState {
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
override val linearId = UniqueIdentifier()
|
override val linearId = UniqueIdentifier()
|
||||||
|
@ -2,12 +2,13 @@ package net.corda.node.services.events
|
|||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.crypto.containsAny
|
import net.corda.core.crypto.containsAny
|
||||||
import net.corda.core.flows.FlowInitiator
|
import net.corda.core.flows.FlowInitiator
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.FlowLogicRefFactory
|
import net.corda.core.flows.FlowLogicRefFactory
|
||||||
import net.corda.core.flows.SchedulableFlow
|
import net.corda.core.flows.SchedulableFlow
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.node.services.linearHeadsOfType
|
import net.corda.core.node.services.linearHeadsOfType
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
@ -46,10 +47,10 @@ class ScheduledFlowTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val participants: List<PublicKey> = listOf(source.owningKey, destination.owningKey)
|
override val participants: List<AbstractParty> = listOf(source, destination)
|
||||||
|
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return participants.any { it.containsAny(ourKeys) }
|
return participants.any { it.owningKey.containsAny(ourKeys) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class DataVendingServiceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `notify of transaction`() {
|
fun `notify of transaction`() {
|
||||||
val (vaultServiceNode, registerNode) = network.createTwoNodes()
|
val (vaultServiceNode, registerNode) = network.createTwoNodes()
|
||||||
val beneficiary = vaultServiceNode.info.legalIdentity.owningKey
|
val beneficiary = vaultServiceNode.info.legalIdentity
|
||||||
val deposit = registerNode.info.legalIdentity.ref(1)
|
val deposit = registerNode.info.legalIdentity.ref(1)
|
||||||
network.runNetwork()
|
network.runNetwork()
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ class DataVendingServiceTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `notify failure`() {
|
fun `notify failure`() {
|
||||||
val (vaultServiceNode, registerNode) = network.createTwoNodes()
|
val (vaultServiceNode, registerNode) = network.createTwoNodes()
|
||||||
val beneficiary = vaultServiceNode.info.legalIdentity.owningKey
|
val beneficiary = vaultServiceNode.info.legalIdentity
|
||||||
val deposit = MEGA_CORP.ref(1)
|
val deposit = MEGA_CORP.ref(1)
|
||||||
network.runNetwork()
|
network.runNetwork()
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.contracts.StateRef
|
|||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.crypto.CompositeKey
|
import net.corda.core.crypto.CompositeKey
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.schemas.PersistentState
|
import net.corda.core.schemas.PersistentState
|
||||||
@ -86,7 +87,7 @@ class HibernateObserverTests {
|
|||||||
override val contract: Contract
|
override val contract: Contract
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
override val participants: List<CompositeKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||||
import net.corda.contracts.testing.fillWithSomeTestCash
|
import net.corda.contracts.testing.fillWithSomeTestCash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.node.services.StatesNotAvailableException
|
import net.corda.core.node.services.StatesNotAvailableException
|
||||||
import net.corda.core.node.services.TxWritableStorageService
|
import net.corda.core.node.services.TxWritableStorageService
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
@ -401,11 +402,11 @@ class NodeVaultServiceTest {
|
|||||||
fun addNoteToTransaction() {
|
fun addNoteToTransaction() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
val freshKey = services.legalIdentityKey
|
val freshKey = services.legalIdentityKey.public
|
||||||
|
|
||||||
// Issue a txn to Send us some Money
|
// Issue a txn to Send us some Money
|
||||||
val usefulTX = TransactionType.General.Builder(null).apply {
|
val usefulTX = TransactionType.General.Builder(null).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
|
||||||
@ -418,7 +419,7 @@ class NodeVaultServiceTest {
|
|||||||
|
|
||||||
// Issue more Money (GBP)
|
// Issue more Money (GBP)
|
||||||
val anotherTX = TransactionType.General.Builder(null).apply {
|
val anotherTX = TransactionType.General.Builder(null).apply {
|
||||||
Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshKey), DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
|
||||||
|
@ -206,9 +206,9 @@ class VaultQueryTests {
|
|||||||
fun `unconsumed states by participants`() {
|
fun `unconsumed states by participants`() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"), participants = listOf(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY))
|
services.fillWithSomeTestLinearStates(2, UniqueIdentifier("TEST"), participants = listOf(MEGA_CORP, MINI_CORP))
|
||||||
services.fillWithSomeTestDeals(listOf("456"), 3, participants = listOf(MEGA_CORP_PUBKEY, BIG_CORP_PUBKEY))
|
services.fillWithSomeTestDeals(listOf("456"), 3, participants = listOf(MEGA_CORP, BIG_CORP))
|
||||||
services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP_PUBKEY, MINI_CORP_PUBKEY))
|
services.fillWithSomeTestDeals(listOf("123", "789"), participants = listOf(BIG_CORP, MINI_CORP))
|
||||||
|
|
||||||
// DOCSTART VaultQueryExample5
|
// DOCSTART VaultQueryExample5
|
||||||
val criteria = VaultQueryCriteria(participantIdentities = listOf(MEGA_CORP.name, MINI_CORP.name))
|
val criteria = VaultQueryCriteria(participantIdentities = listOf(MEGA_CORP.name, MINI_CORP.name))
|
||||||
|
@ -4,17 +4,17 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||||
import net.corda.contracts.testing.*
|
import net.corda.contracts.testing.*
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.node.services.consumedStates
|
import net.corda.core.node.services.consumedStates
|
||||||
import net.corda.core.node.services.unconsumedStates
|
import net.corda.core.node.services.unconsumedStates
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.BOB_KEY
|
import net.corda.core.utilities.BOB
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
||||||
import net.corda.core.utilities.LogHelper
|
import net.corda.core.utilities.LogHelper
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.node.utilities.transaction
|
import net.corda.node.utilities.transaction
|
||||||
import net.corda.testing.BOB_PUBKEY
|
|
||||||
import net.corda.testing.MEGA_CORP
|
import net.corda.testing.MEGA_CORP
|
||||||
import net.corda.testing.MEGA_CORP_KEY
|
import net.corda.testing.MEGA_CORP_KEY
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
@ -79,7 +79,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
val state = w[0].state.data
|
val state = w[0].state.data
|
||||||
assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount)
|
assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount)
|
||||||
assertEquals(services.key.public, state.owner)
|
assertEquals(services.key.public, state.owner.owningKey)
|
||||||
|
|
||||||
assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[2].state.data).amount)
|
assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[2].state.data).amount)
|
||||||
assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[1].state.data).amount)
|
assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[1].state.data).amount)
|
||||||
@ -91,8 +91,9 @@ class VaultWithCashTest {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
// A tx that sends us money.
|
// A tx that sends us money.
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
val usefulTX = TransactionType.General.Builder(null).apply {
|
val usefulTX = TransactionType.General.Builder(null).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), AnonymousParty(freshPublicKey), DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
|
||||||
@ -101,7 +102,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// A tx that spends our money.
|
// A tx that spends our money.
|
||||||
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY)
|
vault.generateSpend(this, 80.DOLLARS, BOB)
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -110,7 +111,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// A tx that doesn't send us anything.
|
// A tx that doesn't send us anything.
|
||||||
val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_KEY.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB, DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -128,13 +129,14 @@ class VaultWithCashTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `issue and attempt double spend`() {
|
fun `issue and attempt double spend`() {
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
|
|
||||||
database.transaction {
|
database.transaction {
|
||||||
// A tx that sends us money.
|
// A tx that sends us money.
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L),
|
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L),
|
||||||
issuedBy = MEGA_CORP.ref(1),
|
issuedBy = MEGA_CORP.ref(1),
|
||||||
issuerKey = MEGA_CORP_KEY,
|
issuerKey = MEGA_CORP_KEY,
|
||||||
ownedBy = freshKey.public)
|
ownedBy = AnonymousParty(freshPublicKey))
|
||||||
println("Cash balance: ${vault.cashBalances[USD]}")
|
println("Cash balance: ${vault.cashBalances[USD]}")
|
||||||
|
|
||||||
assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10)
|
assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10)
|
||||||
@ -149,7 +151,7 @@ class VaultWithCashTest {
|
|||||||
try {
|
try {
|
||||||
val txn1 =
|
val txn1 =
|
||||||
TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
vault.generateSpend(this, 60.DOLLARS, BOB_PUBKEY)
|
vault.generateSpend(this, 60.DOLLARS, BOB)
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -181,7 +183,7 @@ class VaultWithCashTest {
|
|||||||
try {
|
try {
|
||||||
val txn2 =
|
val txn2 =
|
||||||
TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY)
|
vault.generateSpend(this, 80.DOLLARS, BOB)
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -219,12 +221,14 @@ class VaultWithCashTest {
|
|||||||
fun `branching LinearStates fails to verify`() {
|
fun `branching LinearStates fails to verify`() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
|
val freshIdentity = AnonymousParty(freshPublicKey)
|
||||||
val linearId = UniqueIdentifier()
|
val linearId = UniqueIdentifier()
|
||||||
|
|
||||||
// Issue a linear state
|
// Issue a linear state
|
||||||
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -239,12 +243,14 @@ class VaultWithCashTest {
|
|||||||
fun `sequencing LinearStates works`() {
|
fun `sequencing LinearStates works`() {
|
||||||
database.transaction {
|
database.transaction {
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
|
val freshIdentity = AnonymousParty(freshPublicKey)
|
||||||
|
|
||||||
val linearId = UniqueIdentifier()
|
val linearId = UniqueIdentifier()
|
||||||
|
|
||||||
// Issue a linear state
|
// Issue a linear state
|
||||||
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -256,7 +262,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// Move the same state
|
// Move the same state
|
||||||
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshIdentity)))
|
||||||
addInputState(dummyIssue.tx.outRef<LinearState>(0))
|
addInputState(dummyIssue.tx.outRef<LinearState>(0))
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -272,8 +278,9 @@ class VaultWithCashTest {
|
|||||||
fun `spending cash in vault of mixed state types works`() {
|
fun `spending cash in vault of mixed state types works`() {
|
||||||
|
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
database.transaction {
|
database.transaction {
|
||||||
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = freshKey.public)
|
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = AnonymousParty(freshPublicKey))
|
||||||
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L))
|
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L))
|
||||||
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
|
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
|
||||||
val cash = vault.unconsumedStates<Cash.State>()
|
val cash = vault.unconsumedStates<Cash.State>()
|
||||||
@ -287,7 +294,7 @@ class VaultWithCashTest {
|
|||||||
database.transaction {
|
database.transaction {
|
||||||
// A tx that spends our money.
|
// A tx that spends our money.
|
||||||
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val spendTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
vault.generateSpend(this, 80.DOLLARS, BOB_PUBKEY)
|
vault.generateSpend(this, 80.DOLLARS, BOB)
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -305,6 +312,8 @@ class VaultWithCashTest {
|
|||||||
fun `consuming multiple contract state types in same transaction`() {
|
fun `consuming multiple contract state types in same transaction`() {
|
||||||
|
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
|
val freshPublicKey = freshKey.public
|
||||||
|
val freshIdentity = AnonymousParty(freshPublicKey)
|
||||||
database.transaction {
|
database.transaction {
|
||||||
|
|
||||||
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
services.fillWithSomeTestDeals(listOf("123", "456", "789"))
|
||||||
@ -317,8 +326,8 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// Create a txn consuming different contract types
|
// Create a txn consuming different contract types
|
||||||
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(participants = listOf(freshIdentity)))
|
||||||
addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshKey.public)))
|
addOutputState(DummyDealContract.State(ref = "999", participants = listOf(freshIdentity)))
|
||||||
addInputState(linearStates.first())
|
addInputState(linearStates.first())
|
||||||
addInputState(deals.first())
|
addInputState(deals.first())
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.core.crypto.SecureHash
|
|||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.startTrackedFlow
|
import net.corda.core.messaging.startTrackedFlow
|
||||||
import net.corda.core.sizedInputStreamAndHash
|
import net.corda.core.sizedInputStreamAndHash
|
||||||
@ -179,6 +180,6 @@ class AttachmentContract : Contract {
|
|||||||
|
|
||||||
data class State(val hash: SecureHash.SHA256) : ContractState {
|
data class State(val hash: SecureHash.SHA256) : ContractState {
|
||||||
override val contract: Contract = AttachmentContract()
|
override val contract: Contract = AttachmentContract()
|
||||||
override val participants: List<PublicKey> = emptyList()
|
override val participants: List<AbstractParty> = emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ class IRSDemoTest : IntegrationTestCategory {
|
|||||||
val vaultUpdates = proxy.vaultAndUpdates().second
|
val vaultUpdates = proxy.vaultAndUpdates().second
|
||||||
|
|
||||||
val fixingDates = vaultUpdates.map { update ->
|
val fixingDates = vaultUpdates.map { update ->
|
||||||
val irsStates = update.produced.map { it.state.data }.filterIsInstance<InterestRateSwap.State<*>>()
|
val irsStates = update.produced.map { it.state.data }.filterIsInstance<InterestRateSwap.State>()
|
||||||
irsStates.mapNotNull { it.calculation.nextFixingDate() }.max()
|
irsStates.mapNotNull { it.calculation.nextFixingDate() }.max()
|
||||||
}.cache().toBlocking()
|
}.cache().toBlocking()
|
||||||
|
|
||||||
|
@ -40,22 +40,22 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
|
|||||||
|
|
||||||
private val logger = loggerFor<InterestRateSwapAPI>()
|
private val logger = loggerFor<InterestRateSwapAPI>()
|
||||||
|
|
||||||
private fun generateDealLink(deal: InterestRateSwap.State<*>) = "/api/irs/deals/" + deal.common.tradeID
|
private fun generateDealLink(deal: InterestRateSwap.State) = "/api/irs/deals/" + deal.common.tradeID
|
||||||
|
|
||||||
private fun getDealByRef(ref: String): InterestRateSwap.State<*>? {
|
private fun getDealByRef(ref: String): InterestRateSwap.State? {
|
||||||
val (vault, vaultUpdates) = rpc.vaultAndUpdates()
|
val (vault, vaultUpdates) = rpc.vaultAndUpdates()
|
||||||
vaultUpdates.notUsed()
|
vaultUpdates.notUsed()
|
||||||
val states = vault.filterStatesOfType<InterestRateSwap.State<*>>().filter { it.state.data.ref == ref }
|
val states = vault.filterStatesOfType<InterestRateSwap.State>().filter { it.state.data.ref == ref }
|
||||||
return if (states.isEmpty()) null else {
|
return if (states.isEmpty()) null else {
|
||||||
val deals = states.map { it.state.data }
|
val deals = states.map { it.state.data }
|
||||||
return if (deals.isEmpty()) null else deals[0]
|
return if (deals.isEmpty()) null else deals[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAllDeals(): Array<InterestRateSwap.State<*>> {
|
private fun getAllDeals(): Array<InterestRateSwap.State> {
|
||||||
val (vault, vaultUpdates) = rpc.vaultAndUpdates()
|
val (vault, vaultUpdates) = rpc.vaultAndUpdates()
|
||||||
vaultUpdates.notUsed()
|
vaultUpdates.notUsed()
|
||||||
val states = vault.filterStatesOfType<InterestRateSwap.State<*>>()
|
val states = vault.filterStatesOfType<InterestRateSwap.State>()
|
||||||
val swaps = states.map { it.state.data }.toTypedArray()
|
val swaps = states.map { it.state.data }.toTypedArray()
|
||||||
return swaps
|
return swaps
|
||||||
}
|
}
|
||||||
@ -63,14 +63,14 @@ class InterestRateSwapAPI(val rpc: CordaRPCOps) {
|
|||||||
@GET
|
@GET
|
||||||
@Path("deals")
|
@Path("deals")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
fun fetchDeals(): Array<InterestRateSwap.State<*>> = getAllDeals()
|
fun fetchDeals(): Array<InterestRateSwap.State> = getAllDeals()
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("deals")
|
@Path("deals")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
fun storeDeal(newDeal: InterestRateSwap.State<Party>): Response {
|
fun storeDeal(newDeal: InterestRateSwap.State): Response {
|
||||||
return try {
|
return try {
|
||||||
rpc.startFlow(AutoOfferFlow::Requester, newDeal.toAnonymous()).returnValue.getOrThrow()
|
rpc.startFlow(AutoOfferFlow::Requester, newDeal).returnValue.getOrThrow()
|
||||||
Response.created(URI.create(generateDealLink(newDeal))).build()
|
Response.created(URI.create(generateDealLink(newDeal))).build()
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
logger.info("Exception when creating deal: $ex")
|
logger.info("Exception when creating deal: $ex")
|
||||||
|
@ -2,7 +2,8 @@ package net.corda.irs.contract
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.*
|
import net.corda.core.contracts.clauses.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.containsAny
|
||||||
import net.corda.core.flows.FlowLogicRefFactory
|
import net.corda.core.flows.FlowLogicRefFactory
|
||||||
import net.corda.core.identity.AbstractParty
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.AnonymousParty
|
import net.corda.core.identity.AnonymousParty
|
||||||
@ -307,8 +308,8 @@ class InterestRateSwap : Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
open class FixedLeg<P : AbstractParty>(
|
open class FixedLeg(
|
||||||
var fixedRatePayer: P,
|
var fixedRatePayer: AbstractParty,
|
||||||
notional: Amount<Currency>,
|
notional: Amount<Currency>,
|
||||||
paymentFrequency: Frequency,
|
paymentFrequency: Frequency,
|
||||||
effectiveDate: LocalDate,
|
effectiveDate: LocalDate,
|
||||||
@ -335,7 +336,7 @@ class InterestRateSwap : Contract {
|
|||||||
if (other?.javaClass != javaClass) return false
|
if (other?.javaClass != javaClass) return false
|
||||||
if (!super.equals(other)) return false
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
other as FixedLeg<*>
|
other as FixedLeg
|
||||||
|
|
||||||
if (fixedRatePayer != other.fixedRatePayer) return false
|
if (fixedRatePayer != other.fixedRatePayer) return false
|
||||||
if (fixedRate != other.fixedRate) return false
|
if (fixedRate != other.fixedRate) return false
|
||||||
@ -347,7 +348,7 @@ class InterestRateSwap : Contract {
|
|||||||
override fun hashCode() = super.hashCode() + 31 * Objects.hash(fixedRatePayer, fixedRate, rollConvention)
|
override fun hashCode() = super.hashCode() + 31 * Objects.hash(fixedRatePayer, fixedRate, rollConvention)
|
||||||
|
|
||||||
// Can't autogenerate as not a data class :-(
|
// Can't autogenerate as not a data class :-(
|
||||||
fun copy(fixedRatePayer: P = this.fixedRatePayer,
|
fun copy(fixedRatePayer: AbstractParty = this.fixedRatePayer,
|
||||||
notional: Amount<Currency> = this.notional,
|
notional: Amount<Currency> = this.notional,
|
||||||
paymentFrequency: Frequency = this.paymentFrequency,
|
paymentFrequency: Frequency = this.paymentFrequency,
|
||||||
effectiveDate: LocalDate = this.effectiveDate,
|
effectiveDate: LocalDate = this.effectiveDate,
|
||||||
@ -365,17 +366,11 @@ class InterestRateSwap : Contract {
|
|||||||
fixedRatePayer, notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate,
|
fixedRatePayer, notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate,
|
||||||
terminationDateAdjustment, dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay,
|
terminationDateAdjustment, dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay,
|
||||||
paymentCalendar, interestPeriodAdjustment, fixedRate, rollConvention)
|
paymentCalendar, interestPeriodAdjustment, fixedRate, rollConvention)
|
||||||
|
|
||||||
fun toAnonymous(): FixedLeg<AnonymousParty> {
|
|
||||||
return FixedLeg(fixedRatePayer.toAnonymous(), notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, terminationDateAdjustment,
|
|
||||||
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment,
|
|
||||||
fixedRate, rollConvention)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
open class FloatingLeg<P : AbstractParty>(
|
open class FloatingLeg(
|
||||||
var floatingRatePayer: P,
|
var floatingRatePayer: AbstractParty,
|
||||||
notional: Amount<Currency>,
|
notional: Amount<Currency>,
|
||||||
paymentFrequency: Frequency,
|
paymentFrequency: Frequency,
|
||||||
effectiveDate: LocalDate,
|
effectiveDate: LocalDate,
|
||||||
@ -411,7 +406,7 @@ class InterestRateSwap : Contract {
|
|||||||
if (other?.javaClass != javaClass) return false
|
if (other?.javaClass != javaClass) return false
|
||||||
if (!super.equals(other)) return false
|
if (!super.equals(other)) return false
|
||||||
|
|
||||||
other as FloatingLeg<*>
|
other as FloatingLeg
|
||||||
|
|
||||||
if (floatingRatePayer != other.floatingRatePayer) return false
|
if (floatingRatePayer != other.floatingRatePayer) return false
|
||||||
if (rollConvention != other.rollConvention) return false
|
if (rollConvention != other.rollConvention) return false
|
||||||
@ -433,7 +428,7 @@ class InterestRateSwap : Contract {
|
|||||||
index, indexSource, indexTenor)
|
index, indexSource, indexTenor)
|
||||||
|
|
||||||
|
|
||||||
fun copy(floatingRatePayer: P = this.floatingRatePayer,
|
fun copy(floatingRatePayer: AbstractParty = this.floatingRatePayer,
|
||||||
notional: Amount<Currency> = this.notional,
|
notional: Amount<Currency> = this.notional,
|
||||||
paymentFrequency: Frequency = this.paymentFrequency,
|
paymentFrequency: Frequency = this.paymentFrequency,
|
||||||
effectiveDate: LocalDate = this.effectiveDate,
|
effectiveDate: LocalDate = this.effectiveDate,
|
||||||
@ -462,13 +457,6 @@ class InterestRateSwap : Contract {
|
|||||||
paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment, rollConvention,
|
paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment, rollConvention,
|
||||||
fixingRollConvention, resetDayInMonth, fixingPeriod, resetRule, fixingsPerPayment,
|
fixingRollConvention, resetDayInMonth, fixingPeriod, resetRule, fixingsPerPayment,
|
||||||
fixingCalendar, index, indexSource, indexTenor)
|
fixingCalendar, index, indexSource, indexTenor)
|
||||||
|
|
||||||
fun toAnonymous(): FloatingLeg<AnonymousParty> {
|
|
||||||
return FloatingLeg(floatingRatePayer.toAnonymous(), notional, paymentFrequency, effectiveDate, effectiveDateAdjustment, terminationDate, terminationDateAdjustment,
|
|
||||||
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment,
|
|
||||||
rollConvention, fixingRollConvention, resetDayInMonth, fixingPeriodOffset, resetRule, fixingsPerPayment,
|
|
||||||
fixingCalendar, index, indexSource, indexTenor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract) = verifyClause(tx, AllOf(Clauses.Timestamped(), Clauses.Group()), tx.commands.select<Commands>())
|
override fun verify(tx: TransactionForContract) = verifyClause(tx, AllOf(Clauses.Timestamped(), Clauses.Group()), tx.commands.select<Commands>())
|
||||||
@ -478,7 +466,7 @@ class InterestRateSwap : Contract {
|
|||||||
* Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides
|
* Common superclass for IRS contract clauses, which defines behaviour on match/no-match, and provides
|
||||||
* helper functions for the clauses.
|
* helper functions for the clauses.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractIRSClause : Clause<State<*>, Commands, UniqueIdentifier>() {
|
abstract class AbstractIRSClause : Clause<State, Commands, UniqueIdentifier>() {
|
||||||
// These functions may make more sense to use for basket types, but for now let's leave them here
|
// These functions may make more sense to use for basket types, but for now let's leave them here
|
||||||
fun checkLegDates(legs: List<CommonLeg>) {
|
fun checkLegDates(legs: List<CommonLeg>) {
|
||||||
requireThat {
|
requireThat {
|
||||||
@ -494,7 +482,7 @@ class InterestRateSwap : Contract {
|
|||||||
"The notional for all legs must be the same" using legs.all { it.notional == legs[0].notional }
|
"The notional for all legs must be the same" using legs.all { it.notional == legs[0].notional }
|
||||||
}
|
}
|
||||||
for (leg: CommonLeg in legs) {
|
for (leg: CommonLeg in legs) {
|
||||||
if (leg is FixedLeg<*>) {
|
if (leg is FixedLeg) {
|
||||||
requireThat {
|
requireThat {
|
||||||
// TODO: Confirm: would someone really enter a swap with a negative fixed rate?
|
// TODO: Confirm: would someone really enter a swap with a negative fixed rate?
|
||||||
"Fixed leg rate must be positive" using leg.fixedRate.isPositive()
|
"Fixed leg rate must be positive" using leg.fixedRate.isPositive()
|
||||||
@ -520,9 +508,9 @@ class InterestRateSwap : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Group : GroupClauseVerifier<State<*>, Commands, UniqueIdentifier>(AnyOf(Agree(), Fix(), Pay(), Mature())) {
|
class Group : GroupClauseVerifier<State, Commands, UniqueIdentifier>(AnyOf(Agree(), Fix(), Pay(), Mature())) {
|
||||||
// Group by Trade ID for in / out states
|
// Group by Trade ID for in / out states
|
||||||
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State<*>, UniqueIdentifier>> {
|
override fun groupStates(tx: TransactionForContract): List<TransactionForContract.InOutGroup<State, UniqueIdentifier>> {
|
||||||
return tx.groupStates { state -> state.linearId }
|
return tx.groupStates { state -> state.linearId }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -543,12 +531,12 @@ class InterestRateSwap : Contract {
|
|||||||
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java)
|
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Agree::class.java)
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract,
|
override fun verify(tx: TransactionForContract,
|
||||||
inputs: List<State<*>>,
|
inputs: List<State>,
|
||||||
outputs: List<State<*>>,
|
outputs: List<State>,
|
||||||
commands: List<AuthenticatedObject<Commands>>,
|
commands: List<AuthenticatedObject<Commands>>,
|
||||||
groupingKey: UniqueIdentifier?): Set<Commands> {
|
groupingKey: UniqueIdentifier?): Set<Commands> {
|
||||||
val command = tx.commands.requireSingleCommand<Commands.Agree>()
|
val command = tx.commands.requireSingleCommand<Commands.Agree>()
|
||||||
val irs = outputs.filterIsInstance<State<*>>().single()
|
val irs = outputs.filterIsInstance<State>().single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"There are no in states for an agreement" using inputs.isEmpty()
|
"There are no in states for an agreement" using inputs.isEmpty()
|
||||||
"There are events in the fix schedule" using (irs.calculation.fixedLegPaymentSchedule.isNotEmpty())
|
"There are events in the fix schedule" using (irs.calculation.fixedLegPaymentSchedule.isNotEmpty())
|
||||||
@ -579,13 +567,13 @@ class InterestRateSwap : Contract {
|
|||||||
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java)
|
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Refix::class.java)
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract,
|
override fun verify(tx: TransactionForContract,
|
||||||
inputs: List<State<*>>,
|
inputs: List<State>,
|
||||||
outputs: List<State<*>>,
|
outputs: List<State>,
|
||||||
commands: List<AuthenticatedObject<Commands>>,
|
commands: List<AuthenticatedObject<Commands>>,
|
||||||
groupingKey: UniqueIdentifier?): Set<Commands> {
|
groupingKey: UniqueIdentifier?): Set<Commands> {
|
||||||
val command = tx.commands.requireSingleCommand<Commands.Refix>()
|
val command = tx.commands.requireSingleCommand<Commands.Refix>()
|
||||||
val irs = outputs.filterIsInstance<State<*>>().single()
|
val irs = outputs.filterIsInstance<State>().single()
|
||||||
val prevIrs = inputs.filterIsInstance<State<*>>().single()
|
val prevIrs = inputs.filterIsInstance<State>().single()
|
||||||
val paymentDifferences = getFloatingLegPaymentsDifferences(prevIrs.calculation.floatingLegPaymentSchedule, irs.calculation.floatingLegPaymentSchedule)
|
val paymentDifferences = getFloatingLegPaymentsDifferences(prevIrs.calculation.floatingLegPaymentSchedule, irs.calculation.floatingLegPaymentSchedule)
|
||||||
|
|
||||||
// Having both of these tests are "redundant" as far as verify() goes, however, by performing both
|
// Having both of these tests are "redundant" as far as verify() goes, however, by performing both
|
||||||
@ -624,8 +612,8 @@ class InterestRateSwap : Contract {
|
|||||||
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java)
|
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Pay::class.java)
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract,
|
override fun verify(tx: TransactionForContract,
|
||||||
inputs: List<State<*>>,
|
inputs: List<State>,
|
||||||
outputs: List<State<*>>,
|
outputs: List<State>,
|
||||||
commands: List<AuthenticatedObject<Commands>>,
|
commands: List<AuthenticatedObject<Commands>>,
|
||||||
groupingKey: UniqueIdentifier?): Set<Commands> {
|
groupingKey: UniqueIdentifier?): Set<Commands> {
|
||||||
val command = tx.commands.requireSingleCommand<Commands.Pay>()
|
val command = tx.commands.requireSingleCommand<Commands.Pay>()
|
||||||
@ -640,12 +628,12 @@ class InterestRateSwap : Contract {
|
|||||||
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java)
|
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Mature::class.java)
|
||||||
|
|
||||||
override fun verify(tx: TransactionForContract,
|
override fun verify(tx: TransactionForContract,
|
||||||
inputs: List<State<*>>,
|
inputs: List<State>,
|
||||||
outputs: List<State<*>>,
|
outputs: List<State>,
|
||||||
commands: List<AuthenticatedObject<Commands>>,
|
commands: List<AuthenticatedObject<Commands>>,
|
||||||
groupingKey: UniqueIdentifier?): Set<Commands> {
|
groupingKey: UniqueIdentifier?): Set<Commands> {
|
||||||
val command = tx.commands.requireSingleCommand<Commands.Mature>()
|
val command = tx.commands.requireSingleCommand<Commands.Mature>()
|
||||||
val irs = inputs.filterIsInstance<State<*>>().single()
|
val irs = inputs.filterIsInstance<State>().single()
|
||||||
requireThat {
|
requireThat {
|
||||||
"No more fixings to be applied" using (irs.calculation.nextFixingDate() == null)
|
"No more fixings to be applied" using (irs.calculation.nextFixingDate() == null)
|
||||||
"The irs is fully consumed and there is no id matched output state" using outputs.isEmpty()
|
"The irs is fully consumed and there is no id matched output state" using outputs.isEmpty()
|
||||||
@ -667,9 +655,9 @@ class InterestRateSwap : Contract {
|
|||||||
/**
|
/**
|
||||||
* The state class contains the 4 major data classes.
|
* The state class contains the 4 major data classes.
|
||||||
*/
|
*/
|
||||||
data class State<P : AbstractParty>(
|
data class State(
|
||||||
val fixedLeg: FixedLeg<P>,
|
val fixedLeg: FixedLeg,
|
||||||
val floatingLeg: FloatingLeg<P>,
|
val floatingLeg: FloatingLeg,
|
||||||
val calculation: Calculation,
|
val calculation: Calculation,
|
||||||
val common: Common,
|
val common: Common,
|
||||||
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
|
override val linearId: UniqueIdentifier = UniqueIdentifier(common.tradeID)
|
||||||
@ -682,15 +670,15 @@ class InterestRateSwap : Contract {
|
|||||||
|
|
||||||
override val ref = common.tradeID
|
override val ref = common.tradeID
|
||||||
|
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = parties.map { it.owningKey }
|
get() = parties
|
||||||
|
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return fixedLeg.fixedRatePayer.owningKey.containsAny(ourKeys) || floatingLeg.floatingRatePayer.owningKey.containsAny(ourKeys)
|
return fixedLeg.fixedRatePayer.owningKey.containsAny(ourKeys) || floatingLeg.floatingRatePayer.owningKey.containsAny(ourKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val parties: List<AnonymousParty>
|
override val parties: List<AbstractParty>
|
||||||
get() = listOf(fixedLeg.fixedRatePayer.toAnonymous(), floatingLeg.floatingRatePayer.toAnonymous())
|
get() = listOf(fixedLeg.fixedRatePayer, floatingLeg.floatingRatePayer)
|
||||||
|
|
||||||
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
|
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {
|
||||||
val nextFixingOf = nextFixingOf() ?: return null
|
val nextFixingOf = nextFixingOf() ?: return null
|
||||||
@ -700,10 +688,10 @@ class InterestRateSwap : Contract {
|
|||||||
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant)
|
return ScheduledActivity(flowLogicRefFactory.create(FixingFlow.FixingRoleDecider::class.java, thisStateRef), instant)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg.toAnonymous(), fixedLeg.toAnonymous(), calculation, common, notary)
|
override fun generateAgreement(notary: Party): TransactionBuilder = InterestRateSwap().generateAgreement(floatingLeg, fixedLeg, calculation, common, notary)
|
||||||
|
|
||||||
override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) {
|
override fun generateFix(ptx: TransactionBuilder, oldState: StateAndRef<*>, fix: Fix) {
|
||||||
InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this.toAnonymous(), oldState.state.notary), oldState.ref), fix)
|
InterestRateSwap().generateFix(ptx, StateAndRef(TransactionState(this, oldState.state.notary), oldState.ref), fix)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun nextFixingOf(): FixOf? {
|
override fun nextFixingOf(): FixOf? {
|
||||||
@ -737,22 +725,13 @@ class InterestRateSwap : Contract {
|
|||||||
* Just makes printing it out a bit better for those who don't have 80000 column wide monitors.
|
* Just makes printing it out a bit better for those who don't have 80000 column wide monitors.
|
||||||
*/
|
*/
|
||||||
fun prettyPrint() = toString().replace(",", "\n")
|
fun prettyPrint() = toString().replace(",", "\n")
|
||||||
|
|
||||||
fun toAnonymous(): State<AnonymousParty> {
|
|
||||||
return if (this.fixedLeg.fixedRatePayer is AnonymousParty) {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
this as State<AnonymousParty>
|
|
||||||
} else {
|
|
||||||
State(fixedLeg.toAnonymous(), floatingLeg.toAnonymous(), calculation, common, linearId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This generates the agreement state and also the schedules from the initial data.
|
* This generates the agreement state and also the schedules from the initial data.
|
||||||
* Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable.
|
* Note: The day count, interest rate calculation etc are not finished yet, but they are demonstrable.
|
||||||
*/
|
*/
|
||||||
fun generateAgreement(floatingLeg: FloatingLeg<AnonymousParty>, fixedLeg: FixedLeg<AnonymousParty>, calculation: Calculation,
|
fun generateAgreement(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation,
|
||||||
common: Common, notary: Party): TransactionBuilder {
|
common: Common, notary: Party): TransactionBuilder {
|
||||||
|
|
||||||
val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>()
|
val fixedLegPaymentSchedule = LinkedHashMap<LocalDate, FixedRatePaymentEvent>()
|
||||||
@ -816,7 +795,7 @@ class InterestRateSwap : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateFix(tx: TransactionBuilder, irs: StateAndRef<State<AnonymousParty>>, fixing: Fix) {
|
fun generateFix(tx: TransactionBuilder, irs: StateAndRef<State>, fixing: Fix) {
|
||||||
tx.addInputState(irs)
|
tx.addInputState(irs)
|
||||||
val fixedRate = FixedRate(RatioUnit(fixing.value))
|
val fixedRate = FixedRate(RatioUnit(fixing.value))
|
||||||
tx.addOutputState(
|
tx.addOutputState(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.irs.contract
|
package net.corda.irs.contract
|
||||||
|
|
||||||
fun InterestRateSwap.State<*>.exportIRSToCSV(): String =
|
fun InterestRateSwap.State.exportIRSToCSV(): String =
|
||||||
"Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" +
|
"Fixed Leg\n" + FixedRatePaymentEvent.CSVHeader + "\n" +
|
||||||
this.calculation.fixedLegPaymentSchedule.toSortedMap().values.map { it.asCSV() }.joinToString("\n") + "\n" +
|
this.calculation.fixedLegPaymentSchedule.toSortedMap().values.map { it.asCSV() }.joinToString("\n") + "\n" +
|
||||||
"Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" +
|
"Floating Leg\n" + FloatingRatePaymentEvent.CSVHeader + "\n" +
|
||||||
|
@ -79,9 +79,9 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
|
|||||||
return future
|
return future
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadLinearHeads(node: SimulatedNode): Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State<AnonymousParty>>> {
|
private fun loadLinearHeads(node: SimulatedNode): Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State>> {
|
||||||
return node.database.transaction {
|
return node.database.transaction {
|
||||||
node.services.vaultService.linearHeadsOfType<InterestRateSwap.State<AnonymousParty>>()
|
node.services.vaultService.linearHeadsOfType<InterestRateSwap.State>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,8 +90,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
|
|||||||
val node1: SimulatedNode = banks[i]
|
val node1: SimulatedNode = banks[i]
|
||||||
val node2: SimulatedNode = banks[j]
|
val node2: SimulatedNode = banks[j]
|
||||||
|
|
||||||
val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State<AnonymousParty>>> = loadLinearHeads(node1)
|
val swaps: Map<UniqueIdentifier, StateAndRef<InterestRateSwap.State>> = loadLinearHeads(node1)
|
||||||
val theDealRef: StateAndRef<InterestRateSwap.State<AnonymousParty>> = swaps.values.single()
|
val theDealRef: StateAndRef<InterestRateSwap.State> = swaps.values.single()
|
||||||
|
|
||||||
// Do we have any more days left in this deal's lifetime? If not, return.
|
// Do we have any more days left in this deal's lifetime? If not, return.
|
||||||
val nextFixingDate = theDealRef.state.data.calculation.nextFixingDate() ?: return null
|
val nextFixingDate = theDealRef.state.data.calculation.nextFixingDate() ?: return null
|
||||||
@ -121,9 +121,9 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
|
|||||||
// We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't
|
// We load the IRS afresh each time because the leg parts of the structure aren't data classes so they don't
|
||||||
// have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable.
|
// have the convenient copy() method that'd let us make small adjustments. Instead they're partly mutable.
|
||||||
// TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface.
|
// TODO: We should revisit this in post-Excalibur cleanup and fix, e.g. by introducing an interface.
|
||||||
val irs = om.readValue<InterestRateSwap.State<AnonymousParty>>(javaClass.classLoader.getResource("simulation/trade.json"))
|
val irs = om.readValue<InterestRateSwap.State>(javaClass.classLoader.getResource("simulation/trade.json"))
|
||||||
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity.toAnonymous()
|
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity
|
||||||
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity.toAnonymous()
|
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity
|
||||||
|
|
||||||
@InitiatingFlow
|
@InitiatingFlow
|
||||||
class StartDealFlow(val otherParty: Party,
|
class StartDealFlow(val otherParty: Party,
|
||||||
|
@ -16,12 +16,12 @@ import java.time.LocalDate
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
|
fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
|
||||||
return when (irsSelect) {
|
return when (irsSelect) {
|
||||||
1 -> {
|
1 -> {
|
||||||
|
|
||||||
val fixedLeg = InterestRateSwap.FixedLeg(
|
val fixedLeg = InterestRateSwap.FixedLeg(
|
||||||
fixedRatePayer = MEGA_CORP.toAnonymous(),
|
fixedRatePayer = MEGA_CORP,
|
||||||
notional = 15900000.DOLLARS,
|
notional = 15900000.DOLLARS,
|
||||||
paymentFrequency = Frequency.SemiAnnual,
|
paymentFrequency = Frequency.SemiAnnual,
|
||||||
effectiveDate = LocalDate.of(2016, 3, 10),
|
effectiveDate = LocalDate.of(2016, 3, 10),
|
||||||
@ -40,7 +40,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val floatingLeg = InterestRateSwap.FloatingLeg(
|
val floatingLeg = InterestRateSwap.FloatingLeg(
|
||||||
floatingRatePayer = MINI_CORP.toAnonymous(),
|
floatingRatePayer = MINI_CORP,
|
||||||
notional = 15900000.DOLLARS,
|
notional = 15900000.DOLLARS,
|
||||||
paymentFrequency = Frequency.Quarterly,
|
paymentFrequency = Frequency.Quarterly,
|
||||||
effectiveDate = LocalDate.of(2016, 3, 10),
|
effectiveDate = LocalDate.of(2016, 3, 10),
|
||||||
@ -111,7 +111,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
|
|||||||
// I did a mock up start date 10/03/2015 – 10/03/2025 so you have 5 cashflows on float side that have been preset the rest are unknown
|
// I did a mock up start date 10/03/2015 – 10/03/2025 so you have 5 cashflows on float side that have been preset the rest are unknown
|
||||||
|
|
||||||
val fixedLeg = InterestRateSwap.FixedLeg(
|
val fixedLeg = InterestRateSwap.FixedLeg(
|
||||||
fixedRatePayer = MEGA_CORP.toAnonymous(),
|
fixedRatePayer = MEGA_CORP,
|
||||||
notional = 25000000.DOLLARS,
|
notional = 25000000.DOLLARS,
|
||||||
paymentFrequency = Frequency.SemiAnnual,
|
paymentFrequency = Frequency.SemiAnnual,
|
||||||
effectiveDate = LocalDate.of(2015, 3, 10),
|
effectiveDate = LocalDate.of(2015, 3, 10),
|
||||||
@ -130,7 +130,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State<AnonymousParty> {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val floatingLeg = InterestRateSwap.FloatingLeg(
|
val floatingLeg = InterestRateSwap.FloatingLeg(
|
||||||
floatingRatePayer = MINI_CORP.toAnonymous(),
|
floatingRatePayer = MINI_CORP,
|
||||||
notional = 25000000.DOLLARS,
|
notional = 25000000.DOLLARS,
|
||||||
paymentFrequency = Frequency.Quarterly,
|
paymentFrequency = Frequency.Quarterly,
|
||||||
effectiveDate = LocalDate.of(2015, 3, 10),
|
effectiveDate = LocalDate.of(2015, 3, 10),
|
||||||
@ -246,8 +246,8 @@ class IRSTests {
|
|||||||
/**
|
/**
|
||||||
* Utility so I don't have to keep typing this.
|
* Utility so I don't have to keep typing this.
|
||||||
*/
|
*/
|
||||||
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State<AnonymousParty> {
|
fun singleIRS(irsSelector: Int = 1): InterestRateSwap.State {
|
||||||
return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State<AnonymousParty>>().single()
|
return generateIRSTxn(irsSelector).tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,7 +301,7 @@ class IRSTests {
|
|||||||
var previousTXN = generateIRSTxn(1)
|
var previousTXN = generateIRSTxn(1)
|
||||||
previousTXN.toLedgerTransaction(services).verify()
|
previousTXN.toLedgerTransaction(services).verify()
|
||||||
services.recordTransactions(previousTXN)
|
services.recordTransactions(previousTXN)
|
||||||
fun currentIRS() = previousTXN.tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State<AnonymousParty>>().single()
|
fun currentIRS() = previousTXN.tx.outputs.map { it.data }.filterIsInstance<InterestRateSwap.State>().single()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val nextFix: FixOf = currentIRS().nextFixingOf() ?: break
|
val nextFix: FixOf = currentIRS().nextFixingOf() ?: break
|
||||||
@ -381,7 +381,7 @@ class IRSTests {
|
|||||||
|
|
||||||
transaction("Fix") {
|
transaction("Fix") {
|
||||||
input("irs post agreement")
|
input("irs post agreement")
|
||||||
val postAgreement = "irs post agreement".output<InterestRateSwap.State<AnonymousParty>>()
|
val postAgreement = "irs post agreement".output<InterestRateSwap.State>()
|
||||||
output("irs post first fixing") {
|
output("irs post first fixing") {
|
||||||
postAgreement.copy(
|
postAgreement.copy(
|
||||||
postAgreement.fixedLeg,
|
postAgreement.fixedLeg,
|
||||||
@ -688,7 +688,7 @@ class IRSTests {
|
|||||||
transaction("Fix") {
|
transaction("Fix") {
|
||||||
input("irs post agreement1")
|
input("irs post agreement1")
|
||||||
input("irs post agreement2")
|
input("irs post agreement2")
|
||||||
val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State<AnonymousParty>>()
|
val postAgreement1 = "irs post agreement1".output<InterestRateSwap.State>()
|
||||||
output("irs post first fixing1") {
|
output("irs post first fixing1") {
|
||||||
postAgreement1.copy(
|
postAgreement1.copy(
|
||||||
postAgreement1.fixedLeg,
|
postAgreement1.fixedLeg,
|
||||||
@ -697,7 +697,7 @@ class IRSTests {
|
|||||||
postAgreement1.common.copy(tradeID = "t1")
|
postAgreement1.common.copy(tradeID = "t1")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State<AnonymousParty>>()
|
val postAgreement2 = "irs post agreement2".output<InterestRateSwap.State>()
|
||||||
output("irs post first fixing2") {
|
output("irs post first fixing2") {
|
||||||
postAgreement2.copy(
|
postAgreement2.copy(
|
||||||
postAgreement2.fixedLeg,
|
postAgreement2.fixedLeg,
|
||||||
|
@ -13,6 +13,7 @@ import net.corda.core.crypto.generateKeyPair
|
|||||||
import net.corda.core.getOrThrow
|
import net.corda.core.getOrThrow
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
|
import net.corda.core.utilities.ALICE
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.core.utilities.LogHelper
|
import net.corda.core.utilities.LogHelper
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
@ -241,5 +242,5 @@ class NodeInterestRatesTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeTX() = TransactionType.General.Builder(DUMMY_NOTARY).withItems(1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE_PUBKEY `with notary` DUMMY_NOTARY)
|
private fun makeTX() = TransactionType.General.Builder(DUMMY_NOTARY).withItems(1000.DOLLARS.CASH `issued by` DUMMY_CASH_ISSUER `owned by` ALICE `with notary` DUMMY_NOTARY)
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,8 @@ class DummyIssueAndMove(private val notary: Party, private val counterpartyNode:
|
|||||||
}
|
}
|
||||||
serviceHub.recordTransactions(issueTx.toSignedTransaction())
|
serviceHub.recordTransactions(issueTx.toSignedTransaction())
|
||||||
// Move ownership of the asset to the counterparty
|
// Move ownership of the asset to the counterparty
|
||||||
val counterPartyKey = counterpartyNode.owningKey
|
|
||||||
val asset = issueTx.toWireTransaction().outRef<DummyContract.SingleOwnerState>(0)
|
val asset = issueTx.toWireTransaction().outRef<DummyContract.SingleOwnerState>(0)
|
||||||
val moveTx = DummyContract.move(asset, counterPartyKey).apply {
|
val moveTx = DummyContract.move(asset, counterpartyNode).apply {
|
||||||
signWith(myKeyPair)
|
signWith(myKeyPair)
|
||||||
}
|
}
|
||||||
// We don't check signatures because we know that the notary's signature is missing
|
// We don't check signatures because we know that the notary's signature is missing
|
||||||
|
@ -4,9 +4,9 @@ import net.corda.core.contracts.Command
|
|||||||
import net.corda.core.contracts.DealState
|
import net.corda.core.contracts.DealState
|
||||||
import net.corda.core.contracts.TransactionType
|
import net.corda.core.contracts.TransactionType
|
||||||
import net.corda.core.contracts.UniqueIdentifier
|
import net.corda.core.contracts.UniqueIdentifier
|
||||||
import net.corda.core.identity.AnonymousParty
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -16,12 +16,12 @@ import java.security.PublicKey
|
|||||||
* TODO: Merge with the existing demo IRS code.
|
* TODO: Merge with the existing demo IRS code.
|
||||||
*/
|
*/
|
||||||
data class IRSState(val swap: SwapData,
|
data class IRSState(val swap: SwapData,
|
||||||
val buyer: AnonymousParty,
|
val buyer: AbstractParty,
|
||||||
val seller: AnonymousParty,
|
val seller: AbstractParty,
|
||||||
override val contract: OGTrade,
|
override val contract: OGTrade,
|
||||||
override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState {
|
override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState {
|
||||||
override val ref: String = linearId.externalId!! // Same as the constructor for UniqueIdentified
|
override val ref: String = linearId.externalId!! // Same as the constructor for UniqueIdentified
|
||||||
override val parties: List<AnonymousParty> get() = listOf(buyer, seller)
|
override val parties: List<AbstractParty> get() = listOf(buyer, seller)
|
||||||
|
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return parties.flatMap { it.owningKey.keys }.intersect(ourKeys).isNotEmpty()
|
return parties.flatMap { it.owningKey.keys }.intersect(ourKeys).isNotEmpty()
|
||||||
@ -32,6 +32,6 @@ data class IRSState(val swap: SwapData,
|
|||||||
return TransactionType.General.Builder(notary).withItems(state, Command(OGTrade.Commands.Agree(), parties.map { it.owningKey }))
|
return TransactionType.General.Builder(notary).withItems(state, Command(OGTrade.Commands.Agree(), parties.map { it.owningKey }))
|
||||||
}
|
}
|
||||||
|
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = parties.map { it.owningKey }
|
get() = parties
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ data class OGTrade(override val legalContractReference: SecureHash = SecureHash.
|
|||||||
require(inputs.size == 0) { "Inputs must be empty" }
|
require(inputs.size == 0) { "Inputs must be empty" }
|
||||||
require(outputs.size == 1) { "" }
|
require(outputs.size == 1) { "" }
|
||||||
require(outputs[0].buyer != outputs[0].seller)
|
require(outputs[0].buyer != outputs[0].seller)
|
||||||
require(outputs[0].parties.map { it.owningKey }.containsAll(outputs[0].participants))
|
require(outputs[0].parties.containsAll(outputs[0].participants))
|
||||||
require(outputs[0].parties.containsAll(listOf(outputs[0].buyer, outputs[0].seller)))
|
require(outputs[0].parties.containsAll(listOf(outputs[0].buyer, outputs[0].seller)))
|
||||||
require(outputs[0].swap.startDate.isBefore(outputs[0].swap.endDate))
|
require(outputs[0].swap.startDate.isBefore(outputs[0].swap.endDate))
|
||||||
require(outputs[0].swap.notional > BigDecimal(0))
|
require(outputs[0].swap.notional > BigDecimal(0))
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package net.corda.vega.contracts
|
package net.corda.vega.contracts
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.identity.AnonymousParty
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.crypto.keys
|
import net.corda.core.crypto.keys
|
||||||
import net.corda.core.flows.FlowLogicRefFactory
|
import net.corda.core.flows.FlowLogicRefFactory
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.vega.flows.SimmRevaluation
|
import net.corda.vega.flows.SimmRevaluation
|
||||||
@ -19,7 +19,7 @@ import java.time.temporal.ChronoUnit
|
|||||||
*/
|
*/
|
||||||
data class PortfolioState(val portfolio: List<StateRef>,
|
data class PortfolioState(val portfolio: List<StateRef>,
|
||||||
override val contract: PortfolioSwap,
|
override val contract: PortfolioSwap,
|
||||||
private val _parties: Pair<AnonymousParty, AnonymousParty>,
|
private val _parties: Pair<AbstractParty, AbstractParty>,
|
||||||
val valuationDate: LocalDate,
|
val valuationDate: LocalDate,
|
||||||
val valuation: PortfolioValuation? = null,
|
val valuation: PortfolioValuation? = null,
|
||||||
override val linearId: UniqueIdentifier = UniqueIdentifier())
|
override val linearId: UniqueIdentifier = UniqueIdentifier())
|
||||||
@ -27,12 +27,12 @@ data class PortfolioState(val portfolio: List<StateRef>,
|
|||||||
@CordaSerializable
|
@CordaSerializable
|
||||||
data class Update(val portfolio: List<StateRef>? = null, val valuation: PortfolioValuation? = null)
|
data class Update(val portfolio: List<StateRef>? = null, val valuation: PortfolioValuation? = null)
|
||||||
|
|
||||||
override val parties: List<AnonymousParty> get() = _parties.toList()
|
override val parties: List<AbstractParty> get() = _parties.toList()
|
||||||
override val ref: String = linearId.toString()
|
override val ref: String = linearId.toString()
|
||||||
val valuer: AnonymousParty get() = parties[0]
|
val valuer: AbstractParty get() = parties[0]
|
||||||
|
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<AbstractParty>
|
||||||
get() = parties.map { it.owningKey }
|
get() = parties
|
||||||
|
|
||||||
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity {
|
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity {
|
||||||
val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now())
|
val flow = flowLogicRefFactory.create(SimmRevaluation.Initiator::class.java, thisStateRef, LocalDate.now())
|
||||||
|
@ -71,7 +71,7 @@ data class PortfolioSwap(override val legalContractReference: SecureHash = Secur
|
|||||||
"there are no inputs" using (inputs.size == 0)
|
"there are no inputs" using (inputs.size == 0)
|
||||||
"there is one output" using (outputs.size == 1)
|
"there is one output" using (outputs.size == 1)
|
||||||
"valuer must be a party" using (outputs[0].parties.contains(outputs[0].valuer))
|
"valuer must be a party" using (outputs[0].parties.contains(outputs[0].valuer))
|
||||||
"all participants must be parties" using (outputs[0].parties.map { it.owningKey }.containsAll(outputs[0].participants))
|
"all participants must be parties" using (outputs[0].parties.containsAll(outputs[0].participants))
|
||||||
}
|
}
|
||||||
|
|
||||||
return setOf(command.value)
|
return setOf(command.value)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package net.corda.vega.flows
|
package net.corda.vega.flows
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.PluginServiceHub
|
import net.corda.core.node.PluginServiceHub
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -38,7 +38,7 @@ object IRSTradeFlow {
|
|||||||
} else {
|
} else {
|
||||||
Pair(otherParty, myIdentity)
|
Pair(otherParty, myIdentity)
|
||||||
}
|
}
|
||||||
val offer = IRSState(swap, buyer.toAnonymous(), seller.toAnonymous(), OGTrade())
|
val offer = IRSState(swap, buyer, seller, OGTrade())
|
||||||
|
|
||||||
logger.info("Handshake finished, sending IRS trade offer message")
|
logger.info("Handshake finished, sending IRS trade offer message")
|
||||||
val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it }
|
val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it }
|
||||||
|
@ -10,11 +10,11 @@ import com.opengamma.strata.pricer.rate.ImmutableRatesProvider
|
|||||||
import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer
|
import com.opengamma.strata.pricer.swap.DiscountingSwapProductPricer
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.identity.AnonymousParty
|
|
||||||
import net.corda.core.identity.Party
|
|
||||||
import net.corda.core.flows.FlowLogic
|
import net.corda.core.flows.FlowLogic
|
||||||
import net.corda.core.flows.InitiatingFlow
|
import net.corda.core.flows.InitiatingFlow
|
||||||
import net.corda.core.flows.StartableByRPC
|
import net.corda.core.flows.StartableByRPC
|
||||||
|
import net.corda.core.identity.AnonymousParty
|
||||||
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.node.PluginServiceHub
|
import net.corda.core.node.PluginServiceHub
|
||||||
import net.corda.core.node.services.dealsWith
|
import net.corda.core.node.services.dealsWith
|
||||||
import net.corda.core.serialization.CordaSerializable
|
import net.corda.core.serialization.CordaSerializable
|
||||||
@ -85,7 +85,7 @@ object SimmFlow {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
private fun agreePortfolio(portfolio: Portfolio) {
|
private fun agreePortfolio(portfolio: Portfolio) {
|
||||||
logger.info("Agreeing portfolio")
|
logger.info("Agreeing portfolio")
|
||||||
val parties = Pair(myIdentity.toAnonymous(), otherParty.toAnonymous())
|
val parties = Pair(myIdentity, otherParty)
|
||||||
val portfolioState = PortfolioState(portfolio.refs, PortfolioSwap(), parties, valuationDate)
|
val portfolioState = PortfolioState(portfolio.refs, PortfolioSwap(), parties, valuationDate)
|
||||||
|
|
||||||
send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate))
|
send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate))
|
||||||
@ -219,13 +219,6 @@ object SimmFlow {
|
|||||||
return receive<Boolean>(replyToParty).unwrap { it }
|
return receive<Boolean>(replyToParty).unwrap { it }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable
|
|
||||||
private fun agreeValuation(portfolio: Portfolio, asOf: LocalDate, valuer: AnonymousParty): PortfolioValuation {
|
|
||||||
val valuerParty = serviceHub.identityService.partyFromAnonymous(valuer)
|
|
||||||
require(valuerParty != null)
|
|
||||||
return agreeValuation(portfolio, asOf, valuerParty!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* So this is the crux of the Simm Agreement flow
|
* So this is the crux of the Simm Agreement flow
|
||||||
* It needs to do several things - which are mainly defined by the analytics engine we are using - which in this
|
* It needs to do several things - which are mainly defined by the analytics engine we are using - which in this
|
||||||
@ -326,7 +319,7 @@ object SimmFlow {
|
|||||||
@Suspendable
|
@Suspendable
|
||||||
private fun updateValuation(stateRef: StateAndRef<PortfolioState>) {
|
private fun updateValuation(stateRef: StateAndRef<PortfolioState>) {
|
||||||
val portfolio = stateRef.state.data.portfolio.toStateAndRef<IRSState>(serviceHub).toPortfolio()
|
val portfolio = stateRef.state.data.portfolio.toStateAndRef<IRSState>(serviceHub).toPortfolio()
|
||||||
val valuer = stateRef.state.data.valuer
|
val valuer = serviceHub.identityService.partyFromAnonymous(stateRef.state.data.valuer) ?: throw IllegalStateException("Unknown valuer party ${stateRef.state.data.valuer}")
|
||||||
val valuation = agreeValuation(portfolio, offer.valuationDate, valuer)
|
val valuation = agreeValuation(portfolio, offer.valuationDate, valuer)
|
||||||
subFlow(object : StateRevisionFlow.Receiver<PortfolioState.Update>(replyToParty) {
|
subFlow(object : StateRevisionFlow.Receiver<PortfolioState.Update>(replyToParty) {
|
||||||
override fun verifyProposal(proposal: Proposal<PortfolioState.Update>) {
|
override fun verifyProposal(proposal: Proposal<PortfolioState.Update>) {
|
||||||
|
@ -21,7 +21,7 @@ object SimmRevaluation {
|
|||||||
override fun call(): Unit {
|
override fun call(): Unit {
|
||||||
val stateAndRef = serviceHub.vaultService.linearHeadsOfType<PortfolioState>().values.first { it.ref == curStateRef }
|
val stateAndRef = serviceHub.vaultService.linearHeadsOfType<PortfolioState>().values.first { it.ref == curStateRef }
|
||||||
val curState = stateAndRef.state.data
|
val curState = stateAndRef.state.data
|
||||||
val myIdentity = serviceHub.myInfo.legalIdentity.toAnonymous()
|
val myIdentity = serviceHub.myInfo.legalIdentity
|
||||||
if (myIdentity == curState.parties[0]) {
|
if (myIdentity == curState.parties[0]) {
|
||||||
val otherParty = serviceHub.identityService.partyFromAnonymous(curState.parties[1])
|
val otherParty = serviceHub.identityService.partyFromAnonymous(curState.parties[1])
|
||||||
require(otherParty != null) { "Other party must be known by this node" }
|
require(otherParty != null) { "Other party must be known by this node" }
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.vega.flows
|
package net.corda.vega.flows
|
||||||
|
|
||||||
import net.corda.core.contracts.StateAndRef
|
import net.corda.core.contracts.StateAndRef
|
||||||
|
import net.corda.core.identity.AbstractParty
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
import net.corda.core.seconds
|
import net.corda.core.seconds
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -16,7 +17,7 @@ import java.security.PublicKey
|
|||||||
object StateRevisionFlow {
|
object StateRevisionFlow {
|
||||||
class Requester<T>(curStateRef: StateAndRef<RevisionedState<T>>,
|
class Requester<T>(curStateRef: StateAndRef<RevisionedState<T>>,
|
||||||
updatedData: T) : AbstractStateReplacementFlow.Instigator<RevisionedState<T>, RevisionedState<T>, T>(curStateRef, updatedData) {
|
updatedData: T) : AbstractStateReplacementFlow.Instigator<RevisionedState<T>, RevisionedState<T>, T>(curStateRef, updatedData) {
|
||||||
override fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> {
|
override fun assembleTx(): Pair<SignedTransaction, List<AbstractParty>> {
|
||||||
val state = originalState.state.data
|
val state = originalState.state.data
|
||||||
val tx = state.generateRevision(originalState.state.notary, originalState, modification)
|
val tx = state.generateRevision(originalState.state.notary, originalState, modification)
|
||||||
tx.setTime(serviceHub.clock.instant(), 30.seconds)
|
tx.setTime(serviceHub.clock.instant(), 30.seconds)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user