Remove use of full parties from contract states

This commit is contained in:
Ross Nicoll 2017-02-06 11:03:23 +00:00
parent d132d9ed2e
commit 71babc7019
34 changed files with 141 additions and 85 deletions

View File

@ -290,7 +290,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<Party> val parties: List<AnonymousParty>
/** /**
* 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
@ -351,6 +351,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.
*/ */
data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) { data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) {
constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), reference)
override fun toString() = "${party}$reference" override fun toString() = "${party}$reference"
} }

View File

@ -15,6 +15,7 @@ open class AnonymousParty(val owningKey: CompositeKey) {
/** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */ /** Anonymised parties do not include any detail apart from owning key, so equality is dependent solely on the key */
override fun equals(other: Any?): Boolean = other is AnonymousParty && this.owningKey == other.owningKey override fun equals(other: Any?): Boolean = other is AnonymousParty && this.owningKey == other.owningKey
override fun hashCode(): Int = owningKey.hashCode() override fun hashCode(): Int = owningKey.hashCode()
open fun toAnonymous() : AnonymousParty = this
// Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party] // Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party]
// can put in the key and actual name // can put in the key and actual name
override fun toString() = "${owningKey.toBase58String()} <Anonymous>" override fun toString() = "${owningKey.toBase58String()} <Anonymous>"

View File

@ -23,5 +23,6 @@ import java.security.PublicKey
class Party(val name: String, owningKey: CompositeKey) : AnonymousParty(owningKey) { class Party(val name: String, owningKey: CompositeKey) : AnonymousParty(owningKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */ /** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
constructor(name: String, owningKey: PublicKey) : this(name, owningKey.composite) constructor(name: String, owningKey: PublicKey) : this(name, owningKey.composite)
override fun toString() = "${owningKey.toBase58String()} ($name)" override fun toAnonymous(): AnonymousParty = AnonymousParty(owningKey)
override fun toString() = "${owningKey.toBase58String()} (${name})"
} }

View File

@ -216,7 +216,7 @@ interface VaultService {
} }
inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java) inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java)
inline fun <reified T : DealState> VaultService.dealsWith(party: Party) = linearHeadsOfType<T>().values.filter { inline fun <reified T : DealState> VaultService.dealsWith(party: AnonymousParty) = linearHeadsOfType<T>().values.filter {
it.state.data.parties.any { it == party } it.state.data.parties.any { it == party }
} }

View File

@ -428,6 +428,7 @@ fun createKryo(k: Kryo = Kryo()): Kryo {
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>()) addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
// This is required to make all the unit tests pass // This is required to make all the unit tests pass
register(AnonymousParty::class.java)
register(Party::class.java) register(Party::class.java)
// This ensures a NonEmptySetSerializer is constructed with an initial value. // This ensures a NonEmptySetSerializer is constructed with an initial value.

View File

@ -45,6 +45,12 @@ class CompositeKeyGenerator : Generator<CompositeKey>(CompositeKey::class.java)
} }
} }
class AnonymousPartyGenerator : Generator<AnonymousParty>(AnonymousParty::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): AnonymousParty {
return AnonymousParty(CompositeKeyGenerator().generate(random, status))
}
}
class PartyGenerator : Generator<Party>(Party::class.java) { class PartyGenerator : Generator<Party>(Party::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Party { override fun generate(random: SourceOfRandomness, status: GenerationStatus): Party {
return Party(StringGenerator().generate(random, status), CompositeKeyGenerator().generate(random, status)) return Party(StringGenerator().generate(random, status), CompositeKeyGenerator().generate(random, status))
@ -53,7 +59,7 @@ class PartyGenerator : Generator<Party>(Party::class.java) {
class PartyAndReferenceGenerator : Generator<PartyAndReference>(PartyAndReference::class.java) { class PartyAndReferenceGenerator : Generator<PartyAndReference>(PartyAndReference::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PartyAndReference { override fun generate(random: SourceOfRandomness, status: GenerationStatus): PartyAndReference {
return PartyAndReference(PartyGenerator().generate(random, status), OpaqueBytes(random.nextBytes(16))) return PartyAndReference(AnonymousPartyGenerator().generate(random, status), OpaqueBytes(random.nextBytes(16)))
} }
} }

View File

@ -300,7 +300,7 @@ object TwoPartyDealFlow {
// 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.
ptx.setTime(serviceHub.clock.instant(), 30.seconds) ptx.setTime(serviceHub.clock.instant(), 30.seconds)
return Pair(ptx, arrayListOf(deal.parties.single { it.name == serviceHub.myInfo.legalIdentity.name }.owningKey)) return Pair(ptx, arrayListOf(deal.parties.single { it == serviceHub.myInfo.legalIdentity }.owningKey))
} }
} }

View File

@ -192,7 +192,7 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
} }
fun Cash.State.ownedBy(owner: CompositeKey) = copy(owner = owner) fun Cash.State.ownedBy(owner: CompositeKey) = copy(owner = owner)
fun Cash.State.issuedBy(party: AnonymousParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party)))) fun Cash.State.issuedBy(party: AnonymousParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party.toAnonymous()))))
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)))

View File

@ -266,12 +266,19 @@ class Obligation<P> : Contract {
data class State<P>( data class State<P>(
var lifecycle: Lifecycle = Lifecycle.NORMAL, var lifecycle: Lifecycle = Lifecycle.NORMAL,
/** Where the debt originates from (obligor) */ /** Where the debt originates from (obligor) */
val obligor: Party, val obligor: AnonymousParty,
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: CompositeKey val beneficiary: CompositeKey
) : 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: CompositeKey)
: 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<CompositeKey> = setOf(beneficiary) override val exitKeys: Collection<CompositeKey> = setOf(beneficiary)
@ -456,14 +463,14 @@ class Obligation<P> : Contract {
* 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, fun generateIssue(tx: TransactionBuilder,
obligor: Party, obligor: AnonymousParty,
issuanceDef: Terms<P>, issuanceDef: Terms<P>,
pennies: Long, pennies: Long,
beneficiary: CompositeKey, beneficiary: CompositeKey,
notary: Party) { notary: Party) {
check(tx.inputStates().isEmpty()) check(tx.inputStates().isEmpty())
check(tx.outputStates().map { it.data }.sumObligationsOrNull<P>() == null) check(tx.outputStates().map { it.data }.sumObligationsOrNull<P>() == null)
tx.addOutputState(State(Lifecycle.NORMAL, obligor, issuanceDef, pennies, beneficiary), notary) tx.addOutputState(State(Lifecycle.NORMAL, obligor.toAnonymous(), issuanceDef, pennies, beneficiary), notary)
tx.addCommand(Commands.Issue(), obligor.owningKey) tx.addCommand(Commands.Issue(), obligor.owningKey)
} }
@ -475,7 +482,7 @@ class Obligation<P> : Contract {
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL }) "all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL })
} }
val groups = states.groupBy { it.multilateralNetState } val groups = states.groupBy { it.multilateralNetState }
val partyLookup = HashMap<CompositeKey, Party>() val partyLookup = HashMap<CompositeKey, AnonymousParty>()
val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).toSet() val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).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.
@ -700,13 +707,13 @@ fun <P> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<Obligat
= 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> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore)) infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T> Obligation.State<T>.between(parties: Pair<Party, CompositeKey>) = copy(obligor = parties.first, beneficiary = parties.second) infix fun <T> Obligation.State<T>.between(parties: Pair<AnonymousParty, CompositeKey>) = copy(obligor = parties.first.toAnonymous(), beneficiary = parties.second)
infix fun <T> Obligation.State<T>.`owned by`(owner: CompositeKey) = copy(beneficiary = owner) infix fun <T> Obligation.State<T>.`owned by`(owner: CompositeKey) = copy(beneficiary = owner)
infix fun <T> Obligation.State<T>.`issued by`(party: Party) = copy(obligor = party) infix fun <T> Obligation.State<T>.`issued by`(party: AnonymousParty) = copy(obligor = party.toAnonymous())
// For Java users: // For Java users:
@Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: CompositeKey) = copy(beneficiary = owner) @Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: CompositeKey) = copy(beneficiary = owner)
@Suppress("unused") fun <T> Obligation.State<T>.issuedBy(party: Party) = copy(obligor = party) @Suppress("unused") fun <T> Obligation.State<T>.issuedBy(party: AnonymousParty) = copy(obligor = party)
/** A randomly generated key. */ /** A randomly generated key. */
val DUMMY_OBLIGATION_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) } val DUMMY_OBLIGATION_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
@ -716,4 +723,4 @@ val DUMMY_OBLIGATION_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_OBLIGATION
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, token.OBLIGATION_DEF, quantity, NullCompositeKey) get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER.toAnonymous(), token.OBLIGATION_DEF, quantity, NullCompositeKey)

View File

@ -28,11 +28,6 @@ class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService {
override fun partyFromKey(key: CompositeKey): Party? = keyToParties[key] override fun partyFromKey(key: CompositeKey): Party? = keyToParties[key]
override fun partyFromName(name: String): Party? = nameToParties[name] override fun partyFromName(name: String): Party? = nameToParties[name]
override fun partyFromAnonymous(party: AnonymousParty): Party? { override fun partyFromAnonymous(party: AnonymousParty): Party? = partyFromKey(party.owningKey)
return if (party is Party)
party
else
partyFromKey(party.owningKey)
}
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)
} }

View File

@ -16,10 +16,7 @@ import de.javakaffee.kryoserializers.guava.*
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.ErrorOr import net.corda.core.ErrorOr
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.IllegalFlowLogicException import net.corda.core.flows.IllegalFlowLogicException
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
@ -140,6 +137,7 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
register(SignedTransaction::class.java, ImmutableClassSerializer(SignedTransaction::class)) register(SignedTransaction::class.java, ImmutableClassSerializer(SignedTransaction::class))
register(WireTransaction::class.java, WireTransactionSerializer) register(WireTransaction::class.java, WireTransactionSerializer)
register(SerializedBytes::class.java, SerializedBytesSerializer) register(SerializedBytes::class.java, SerializedBytesSerializer)
register(AnonymousParty::class.java)
register(Party::class.java) register(Party::class.java)
register(Array<Any>(0,{}).javaClass) register(Array<Any>(0,{}).javaClass)
register(Class::class.java, ClassSerializer) register(Class::class.java, ClassSerializer)

View File

@ -30,16 +30,20 @@ import java.time.LocalDateTime
object JsonSupport { object JsonSupport {
interface PartyObjectMapper { interface PartyObjectMapper {
fun partyFromName(partyName: String): Party? fun partyFromName(partyName: String): Party?
fun partyFromKey(owningKey: CompositeKey): Party?
} }
class RpcObjectMapper(val rpc: CordaRPCOps) : PartyObjectMapper, ObjectMapper() { class RpcObjectMapper(val rpc: CordaRPCOps) : PartyObjectMapper, ObjectMapper() {
override fun partyFromName(partyName: String): Party? = rpc.partyFromName(partyName) override fun partyFromName(partyName: String): Party? = rpc.partyFromName(partyName)
override fun partyFromKey(owningKey: CompositeKey): Party? = rpc.partyFromKey(owningKey)
} }
class IdentityObjectMapper(val identityService: IdentityService) : PartyObjectMapper, ObjectMapper(){ class IdentityObjectMapper(val identityService: IdentityService) : PartyObjectMapper, ObjectMapper(){
override fun partyFromName(partyName: String) = identityService.partyFromName(partyName) override fun partyFromName(partyName: String): Party? = identityService.partyFromName(partyName)
override fun partyFromKey(owningKey: CompositeKey): Party? = identityService.partyFromKey(owningKey)
} }
class NoPartyObjectMapper: PartyObjectMapper, ObjectMapper() { class NoPartyObjectMapper: PartyObjectMapper, ObjectMapper() {
override fun partyFromName(partyName: String) = throw UnsupportedOperationException() override fun partyFromName(partyName: String): Party? = throw UnsupportedOperationException()
override fun partyFromKey(owningKey: CompositeKey): Party? = throw UnsupportedOperationException()
} }
val javaTimeModule: Module by lazy { val javaTimeModule: Module by lazy {
@ -53,6 +57,8 @@ object JsonSupport {
val cordaModule: Module by lazy { val cordaModule: Module by lazy {
SimpleModule("core").apply { SimpleModule("core").apply {
addSerializer(AnonymousParty::class.java, AnonymousPartySerializer)
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)
addSerializer(BigDecimal::class.java, ToStringSerializer) addSerializer(BigDecimal::class.java, ToStringSerializer)
@ -120,6 +126,26 @@ object JsonSupport {
} }
object AnonymousPartySerializer : JsonSerializer<AnonymousParty>() {
override fun serialize(obj: AnonymousParty, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.owningKey.toBase58String())
}
}
object AnonymousPartyDeserializer : JsonDeserializer<AnonymousParty>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): AnonymousParty {
if (parser.currentToken == JsonToken.FIELD_NAME) {
parser.nextToken()
}
val mapper = parser.codec as PartyObjectMapper
// TODO this needs to use some industry identifier(s) instead of these keys
val key = CompositeKey.parseFromBase58(parser.text)
val party = mapper.partyFromKey(key) ?: throw JsonParseException(parser, "Could not find a Party with key ${parser.text}")
return party.toAnonymous()
}
}
object PartySerializer : JsonSerializer<Party>() { object PartySerializer : JsonSerializer<Party>() {
override fun serialize(obj: Party, generator: JsonGenerator, provider: SerializerProvider) { override fun serialize(obj: Party, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.name) generator.writeString(obj.name)

View File

@ -2,6 +2,7 @@ package net.corda.irs
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.Futures
import net.corda.core.crypto.Party
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.irs.api.NodeInterestRates import net.corda.irs.api.NodeInterestRates
@ -44,7 +45,7 @@ class IRSDemoTest : IntegrationTestCategory {
val nextFixingDates = getFixingDateObservable(nodeA.configuration) val nextFixingDates = getFixingDateObservable(nodeA.configuration)
runUploadRates(controllerAddr) runUploadRates(controllerAddr)
runTrade(nodeAAddr) runTrade(nodeAAddr, nodeA.nodeInfo.legalIdentity, nodeB.nodeInfo.legalIdentity)
// Wait until the initial trade and all scheduled fixings up to the current date have finished // Wait until the initial trade and all scheduled fixings up to the current date have finished
nextFixingDates.first { it == null || it > currentDate } nextFixingDates.first { it == null || it > currentDate }
@ -72,9 +73,11 @@ class IRSDemoTest : IntegrationTestCategory {
assert(putJson(url, "\"$futureDate\"")) assert(putJson(url, "\"$futureDate\""))
} }
private fun runTrade(nodeAddr: HostAndPort) { private fun runTrade(nodeAddr: HostAndPort, fixedRatePayer: Party, floatingRatePayer: Party) {
val fileContents = IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream("example-irs-trade.json")) val fileContents = IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream("example-irs-trade.json"))
val tradeFile = fileContents.replace("tradeXXX", "trade1") val tradeFile = fileContents.replace("tradeXXX", "trade1")
.replace("fixedRatePayerKey", fixedRatePayer.owningKey.toBase58String())
.replace("floatingRatePayerKey", floatingRatePayer.owningKey.toBase58String())
val url = URL("http://$nodeAddr/api/irs/deals") val url = URL("http://$nodeAddr/api/irs/deals")
assert(postJson(url, tradeFile)) assert(postJson(url, tradeFile))
} }

View File

@ -2,6 +2,7 @@ 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.AnonymousParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -304,7 +305,7 @@ class InterestRateSwap() : Contract {
} }
open class FixedLeg( open class FixedLeg(
var fixedRatePayer: Party, var fixedRatePayer: AnonymousParty,
notional: Amount<Currency>, notional: Amount<Currency>,
paymentFrequency: Frequency, paymentFrequency: Frequency,
effectiveDate: LocalDate, effectiveDate: LocalDate,
@ -343,7 +344,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: Party = this.fixedRatePayer, fun copy(fixedRatePayer: AnonymousParty = 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,7 +366,7 @@ class InterestRateSwap() : Contract {
} }
open class FloatingLeg( open class FloatingLeg(
var floatingRatePayer: Party, var floatingRatePayer: AnonymousParty,
notional: Amount<Currency>, notional: Amount<Currency>,
paymentFrequency: Frequency, paymentFrequency: Frequency,
effectiveDate: LocalDate, effectiveDate: LocalDate,
@ -423,7 +424,7 @@ class InterestRateSwap() : Contract {
index, indexSource, indexTenor) index, indexSource, indexTenor)
fun copy(floatingRatePayer: Party = this.floatingRatePayer, fun copy(floatingRatePayer: AnonymousParty = 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,
@ -672,7 +673,7 @@ class InterestRateSwap() : Contract {
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<Party> override val parties: List<AnonymousParty>
get() = listOf(fixedLeg.fixedRatePayer, floatingLeg.floatingRatePayer) get() = listOf(fixedLeg.fixedRatePayer, floatingLeg.floatingRatePayer)
override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? { override fun nextScheduledActivity(thisStateRef: StateRef, flowLogicRefFactory: FlowLogicRefFactory): ScheduledActivity? {

View File

@ -2,7 +2,7 @@ package net.corda.irs.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.crypto.Party import net.corda.core.crypto.AnonymousParty
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
@ -65,7 +65,7 @@ object AutoOfferFlow {
require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" }
val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity
// need to pick which ever party is not us // need to pick which ever party is not us
val otherParty = notUs(dealToBeOffered.parties).single() val otherParty = notUs(dealToBeOffered.parties).map { serviceHub.identityService.partyFromAnonymous(it) }.requireNoNulls().single()
progressTracker.currentStep = DEALING progressTracker.currentStep = DEALING
val myKey = serviceHub.legalIdentityKey val myKey = serviceHub.legalIdentityKey
val instigator = Instigator( val instigator = Instigator(
@ -78,8 +78,8 @@ object AutoOfferFlow {
return stx return stx
} }
private fun notUs(parties: List<Party>): List<Party> { private fun <T: AnonymousParty> notUs(parties: List<T>): List<T> {
val notUsParties: MutableList<Party> = arrayListOf() val notUsParties: MutableList<T> = arrayListOf()
for (party in parties) { for (party in parties) {
if (serviceHub.myInfo.legalIdentity != party) { if (serviceHub.myInfo.legalIdentity != party) {
notUsParties.add(party) notUsParties.add(party)

View File

@ -151,8 +151,9 @@ object FixingFlow {
val myKey = serviceHub.myInfo.legalIdentity.owningKey val myKey = serviceHub.myInfo.legalIdentity.owningKey
if (parties[0].owningKey == myKey) { if (parties[0].owningKey == myKey) {
val fixing = FixingSession(ref, fixableDeal.oracleType) val fixing = FixingSession(ref, fixableDeal.oracleType)
val counterparty = serviceHub.identityService.partyFromAnonymous(parties[1]) ?: throw IllegalStateException("Cannot resolve floater party")
// Start the Floater which will then kick-off the Fixer // Start the Floater which will then kick-off the Fixer
subFlow(Floater(parties[1], fixing)) subFlow(Floater(counterparty, fixing))
} }
} }
} }

View File

@ -117,8 +117,8 @@ class IRSSimulation(networkSendManuallyPumped: Boolean, runAsync: Boolean, laten
// 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>(javaClass.classLoader.getResource("simulation/trade.json")) val irs = om.readValue<InterestRateSwap.State>(javaClass.classLoader.getResource("simulation/trade.json"))
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity.toAnonymous()
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity.toAnonymous()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val acceptorTx = node2.initiateSingleShotFlow(Instigator::class) { Acceptor(it) }.flatMap { val acceptorTx = node2.initiateSingleShotFlow(Instigator::class) { Acceptor(it) }.flatMap {

View File

@ -75,8 +75,9 @@ abstract class Simulation(val networkSendManuallyPumped: Boolean,
} }
fun createAll(): List<SimulatedNode> { fun createAll(): List<SimulatedNode> {
return bankLocations.map { return bankLocations.mapIndexed { i, location ->
network.createNode(networkMap.info.address, start = false, nodeFactory = this) as SimulatedNode // Use deterministic seeds so the simulation is stable. Needed so that party owning keys are stable.
network.createNode(networkMap.info.address, start = false, nodeFactory = this, entropyRoot = BigInteger.valueOf(i.toLong())) as SimulatedNode
} }
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"fixedLeg": { "fixedLeg": {
"fixedRatePayer": "Bank A", "fixedRatePayer": "fixedRatePayerKey",
"notional": { "notional": {
"quantity": 2500000000, "quantity": 2500000000,
"token": "EUR" "token": "EUR"
@ -25,7 +25,7 @@
"interestPeriodAdjustment": "Adjusted" "interestPeriodAdjustment": "Adjusted"
}, },
"floatingLeg": { "floatingLeg": {
"floatingRatePayer": "Bank B", "floatingRatePayer": "floatingRatePayerKey",
"notional": { "notional": {
"quantity": 2500000000, "quantity": 2500000000,
"token": "EUR" "token": "EUR"

View File

@ -1,6 +1,6 @@
{ {
"fixedLeg": { "fixedLeg": {
"fixedRatePayer": "Bank A", "fixedRatePayer": "bzs7kfAFKFTtGhxNHeN7eiqufP9Q3p9hDvSTi8AyoRAwiLK8ZZ",
"notional": { "notional": {
"quantity": 2500000000, "quantity": 2500000000,
"token": "USD" "token": "USD"
@ -25,7 +25,7 @@
"interestPeriodAdjustment": "Adjusted" "interestPeriodAdjustment": "Adjusted"
}, },
"floatingLeg": { "floatingLeg": {
"floatingRatePayer": "Bank B", "floatingRatePayer": "bzs7kf3Zc6J8mgNyH2ZddNRp3wzQt8MnPMT4zMYWgouHB4Uro5",
"notional": { "notional": {
"quantity": 2500000000, "quantity": 2500000000,
"token": "USD" "token": "USD"

View File

@ -20,7 +20,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
1 -> { 1 -> {
val fixedLeg = InterestRateSwap.FixedLeg( val fixedLeg = InterestRateSwap.FixedLeg(
fixedRatePayer = MEGA_CORP, fixedRatePayer = MEGA_CORP.toAnonymous(),
notional = 15900000.DOLLARS, notional = 15900000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual, paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2016, 3, 10), effectiveDate = LocalDate.of(2016, 3, 10),
@ -39,7 +39,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
) )
val floatingLeg = InterestRateSwap.FloatingLeg( val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP, floatingRatePayer = MINI_CORP.toAnonymous(),
notional = 15900000.DOLLARS, notional = 15900000.DOLLARS,
paymentFrequency = Frequency.Quarterly, paymentFrequency = Frequency.Quarterly,
effectiveDate = LocalDate.of(2016, 3, 10), effectiveDate = LocalDate.of(2016, 3, 10),
@ -110,7 +110,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
// 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, fixedRatePayer = MEGA_CORP.toAnonymous(),
notional = 25000000.DOLLARS, notional = 25000000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual, paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2015, 3, 10), effectiveDate = LocalDate.of(2015, 3, 10),
@ -129,7 +129,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
) )
val floatingLeg = InterestRateSwap.FloatingLeg( val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP, floatingRatePayer = MINI_CORP.toAnonymous(),
notional = 25000000.DOLLARS, notional = 25000000.DOLLARS,
paymentFrequency = Frequency.Quarterly, paymentFrequency = Frequency.Quarterly,
effectiveDate = LocalDate.of(2015, 3, 10), effectiveDate = LocalDate.of(2015, 3, 10),

View File

@ -4,6 +4,7 @@ import com.opengamma.strata.basics.currency.MultiCurrencyAmount
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.filterStatesOfType import net.corda.core.contracts.filterStatesOfType
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -33,7 +34,7 @@ class PortfolioApi(val rpc: CordaRPCOps) {
private val ownParty: Party get() = rpc.nodeIdentity().legalIdentity private val ownParty: Party get() = rpc.nodeIdentity().legalIdentity
private val portfolioUtils = PortfolioApiUtils(ownParty) private val portfolioUtils = PortfolioApiUtils(ownParty)
private inline fun <reified T : DealState> dealsWith(party: Party): List<StateAndRef<T>> { private inline fun <reified T : DealState> dealsWith(party: AnonymousParty): List<StateAndRef<T>> {
return rpc.vaultAndUpdates().first.filterStatesOfType<T>().filter { it.state.data.parties.any { it == party } } return rpc.vaultAndUpdates().first.filterStatesOfType<T>().filter { it.state.data.parties.any { it == party } }
} }
@ -220,7 +221,7 @@ class PortfolioApi(val rpc: CordaRPCOps) {
return withParty(partyName) { party -> return withParty(partyName) { party ->
withPortfolio(party) { state -> withPortfolio(party) { state ->
if (state.valuation != null) { if (state.valuation != null) {
val isValuer = state.valuer.name == ownParty.name val isValuer = state.valuer as AnonymousParty == ownParty
val rawMtm = state.valuation.presentValues.map { val rawMtm = state.valuation.presentValues.map {
it.value.amounts.first().amount it.value.amounts.first().amount
}.reduce { a, b -> a + b } }.reduce { a, b -> a + b }

View File

@ -122,7 +122,7 @@ class PortfolioApiUtils(private val ownParty: Party) {
val ref: String) val ref: String)
fun createTradeView(state: IRSState): TradeView { fun createTradeView(state: IRSState): TradeView {
val trade = if (state.buyer.name == ownParty.name) state.swap.toFloatingLeg() else state.swap.toFloatingLeg() val trade = if (state.buyer == ownParty) state.swap.toFloatingLeg() else state.swap.toFloatingLeg()
val fixedLeg = trade.product.legs.first { it.type == SwapLegType.FIXED } as RateCalculationSwapLeg val fixedLeg = trade.product.legs.first { it.type == SwapLegType.FIXED } as RateCalculationSwapLeg
val floatingLeg = trade.product.legs.first { it.type != SwapLegType.FIXED } as RateCalculationSwapLeg val floatingLeg = trade.product.legs.first { it.type != SwapLegType.FIXED } as RateCalculationSwapLeg
val fixedRate = fixedLeg.calculation as FixedRateCalculation val fixedRate = fixedLeg.calculation as FixedRateCalculation
@ -130,7 +130,7 @@ class PortfolioApiUtils(private val ownParty: Party) {
return TradeView( return TradeView(
fixedLeg = mapOf( fixedLeg = mapOf(
"fixedRatePayer" to state.buyer.name, "fixedRatePayer" to state.buyer.owningKey.toBase58String(),
"notional" to mapOf( "notional" to mapOf(
"token" to fixedLeg.currency.code, "token" to fixedLeg.currency.code,
"quantity" to fixedLeg.notionalSchedule.amount.initialValue "quantity" to fixedLeg.notionalSchedule.amount.initialValue
@ -146,7 +146,7 @@ class PortfolioApiUtils(private val ownParty: Party) {
"paymentCalendar" to mapOf<String, Any>() // TODO "paymentCalendar" to mapOf<String, Any>() // TODO
), ),
floatingLeg = mapOf( floatingLeg = mapOf(
"floatingRatePayer" to state.seller.name, "floatingRatePayer" to state.seller.owningKey.toBase58String(),
"notional" to mapOf( "notional" to mapOf(
"token" to floatingLeg.currency.code, "token" to floatingLeg.currency.code,
"quantity" to floatingLeg.notionalSchedule.amount.initialValue "quantity" to floatingLeg.notionalSchedule.amount.initialValue

View File

@ -27,7 +27,7 @@ data class SwapDataModel(
*/ */
fun toData(buyer: Party, seller: Party): SwapData { fun toData(buyer: Party, seller: Party): SwapData {
return SwapData( return SwapData(
Pair("swap", id), Pair("party", buyer.name), Pair("party", seller.name), description, tradeDate, convention, startDate, endDate, notional, fixedRate Pair("swap", id), Pair("party", buyer.owningKey), Pair("party", seller.owningKey), description, tradeDate, convention, startDate, endDate, notional, fixedRate
) )
} }
} }

View File

@ -30,7 +30,7 @@ data class SwapDataView(
fun SwapData.toView(viewingParty: Party, portfolio: Portfolio? = null, fun SwapData.toView(viewingParty: Party, portfolio: Portfolio? = null,
presentValue: MultiCurrencyAmount? = null, presentValue: MultiCurrencyAmount? = null,
IM: InitialMarginTriple? = null): SwapDataView { IM: InitialMarginTriple? = null): SwapDataView {
val isBuyer = viewingParty.name == buyer.second val isBuyer = viewingParty.owningKey == buyer.second
val trade = if (isBuyer) toFixedLeg() else toFloatingLeg() val trade = if (isBuyer) toFixedLeg() else toFloatingLeg()
val leg = getLegForParty(viewingParty) val leg = getLegForParty(viewingParty)
val sdv = SwapDataView( val sdv = SwapDataView(

View File

@ -4,6 +4,7 @@ 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.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -15,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: Party, val buyer: AnonymousParty,
val seller: Party, val seller: AnonymousParty,
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<Party> get() = listOf(buyer, seller) override val parties: List<AnonymousParty> 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()

View File

@ -1,6 +1,7 @@
package net.corda.vega.contracts package net.corda.vega.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
@ -17,16 +18,16 @@ 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<Party, Party>, private val _parties: Pair<AnonymousParty, AnonymousParty>,
val valuationDate: LocalDate, val valuationDate: LocalDate,
val valuation: PortfolioValuation? = null, val valuation: PortfolioValuation? = null,
override val linearId: UniqueIdentifier = UniqueIdentifier()) override val linearId: UniqueIdentifier = UniqueIdentifier())
: RevisionedState<PortfolioState.Update>, SchedulableState, DealState { : RevisionedState<PortfolioState.Update>, SchedulableState, DealState {
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<Party> get() = _parties.toList() override val parties: List<AnonymousParty> get() = _parties.toList()
override val ref: String = linearId.toString() override val ref: String = linearId.toString()
val valuer: Party get() = parties[0] val valuer: AnonymousParty get() = parties[0]
override val participants: List<CompositeKey> override val participants: List<CompositeKey>
get() = parties.map { it.owningKey } get() = parties.map { it.owningKey }

View File

@ -9,6 +9,8 @@ import com.opengamma.strata.product.common.BuySell
import com.opengamma.strata.product.swap.SwapTrade import com.opengamma.strata.product.swap.SwapTrade
import com.opengamma.strata.product.swap.type.FixedIborSwapConvention import com.opengamma.strata.product.swap.type.FixedIborSwapConvention
import com.opengamma.strata.product.swap.type.FixedIborSwapConventions import com.opengamma.strata.product.swap.type.FixedIborSwapConventions
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.math.BigDecimal import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
@ -36,8 +38,8 @@ data class FloatingLeg(val _notional: BigDecimal, override val notional: BigDeci
*/ */
data class SwapData( data class SwapData(
val id: Pair<String, String>, val id: Pair<String, String>,
val buyer: Pair<String, String>, val buyer: Pair<String, CompositeKey>,
val seller: Pair<String, String>, val seller: Pair<String, CompositeKey>,
val description: String, val description: String,
val tradeDate: LocalDate, val tradeDate: LocalDate,
val convention: String, val convention: String,
@ -46,8 +48,8 @@ data class SwapData(
val notional: BigDecimal, val notional: BigDecimal,
val fixedRate: BigDecimal) { val fixedRate: BigDecimal) {
fun getLegForParty(party: Party): Leg { fun getLegForParty(party: AnonymousParty): Leg {
return if (party.name == buyer.second) FixedLeg(notional) else FloatingLeg(notional) return if (party == buyer.second) FixedLeg(notional) else FloatingLeg(notional)
} }
fun toFixedLeg(): SwapTrade { fun toFixedLeg(): SwapTrade {
@ -58,11 +60,11 @@ data class SwapData(
return getTrade(BuySell.SELL, Pair("party", seller.second)) return getTrade(BuySell.SELL, Pair("party", seller.second))
} }
private fun getTrade(buySell: BuySell, party: Pair<String, String>): SwapTrade { private fun getTrade(buySell: BuySell, party: Pair<String, CompositeKey>): SwapTrade {
val tradeInfo = TradeInfo.builder() val tradeInfo = TradeInfo.builder()
.id(StandardId.of(id.first, id.second)) .id(StandardId.of(id.first, id.second))
.addAttribute(TradeAttributeType.DESCRIPTION, description) .addAttribute(TradeAttributeType.DESCRIPTION, description)
.counterparty(StandardId.of(party.first, party.second)) .counterparty(StandardId.of(party.first, party.second.toBase58String()))
.build() .build()
// TODO: Fix below to be correct - change tenor and reference data // TODO: Fix below to be correct - change tenor and reference data
return getSwapConvention(convention).createTrade(startDate, Tenor.TENOR_4Y, buySell, notional.toDouble(), fixedRate.toDouble(), ReferenceData.standard()) return getSwapConvention(convention).createTrade(startDate, Tenor.TENOR_4Y, buySell, notional.toDouble(), fixedRate.toDouble(), ReferenceData.standard())

View File

@ -22,12 +22,12 @@ object IRSTradeFlow {
val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity
val myIdentity = serviceHub.myInfo.legalIdentity val myIdentity = serviceHub.myInfo.legalIdentity
val (buyer, seller) = val (buyer, seller) =
if (swap.buyer.second == myIdentity.name) { if (swap.buyer.second == myIdentity.owningKey) {
Pair(myIdentity, otherParty) Pair(myIdentity, otherParty)
} else { } else {
Pair(otherParty, myIdentity) Pair(otherParty, myIdentity)
} }
val offer = IRSState(swap, buyer, seller, OGTrade()) val offer = IRSState(swap, buyer.toAnonymous(), seller.toAnonymous(), OGTrade())
logger.info("Handshake finished, sending IRS trade offer message") logger.info("Handshake finished, sending IRS trade offer message")
val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it } val otherPartyAgreeFlag = sendAndReceive<Boolean>(otherParty, OfferMessage(notary, offer)).unwrap { it }

View File

@ -12,6 +12,7 @@ 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.crypto.AnonymousParty
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.messaging.Ack import net.corda.core.messaging.Ack
@ -59,7 +60,7 @@ object SimmFlow {
@Suspendable @Suspendable
override fun call(): RevisionedState<PortfolioState.Update> { override fun call(): RevisionedState<PortfolioState.Update> {
logger.debug("Calling from: ${serviceHub.myInfo.legalIdentity.name}. Sending to: ${otherParty.name}") logger.debug("Calling from: ${serviceHub.myInfo.legalIdentity}. Sending to: ${otherParty}")
require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" } require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" }
notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity
myIdentity = serviceHub.myInfo.legalIdentity myIdentity = serviceHub.myInfo.legalIdentity
@ -80,7 +81,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, otherParty) val parties = Pair(myIdentity.toAnonymous(), otherParty.toAnonymous())
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))
@ -102,8 +103,9 @@ object SimmFlow {
logger.info("Agreeing valuations") logger.info("Agreeing valuations")
val state = stateRef.state.data val state = stateRef.state.data
val portfolio = state.portfolio.toStateAndRef<IRSState>(serviceHub).toPortfolio() val portfolio = state.portfolio.toStateAndRef<IRSState>(serviceHub).toPortfolio()
val valuer = state.valuer val valuer = serviceHub.identityService.partyFromAnonymous(state.valuer)
val valuation = agreeValuation(portfolio, valuationDate, valuer) require(valuer != null) { "Valuer party must be known to this node" }
val valuation = agreeValuation(portfolio, valuationDate, valuer!!)
val update = PortfolioState.Update(valuation = valuation) val update = PortfolioState.Update(valuation = valuation)
return subFlow(StateRevisionFlow.Requester(stateRef, update), shareParentSessions = true).state.data return subFlow(StateRevisionFlow.Requester(stateRef, update), shareParentSessions = true).state.data
} }
@ -213,6 +215,13 @@ object SimmFlow {
return receive<Boolean>(replyToParty).unwrap { it == true } return receive<Boolean>(replyToParty).unwrap { it == true }
} }
@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

View File

@ -17,10 +17,11 @@ 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 val myIdentity = serviceHub.myInfo.legalIdentity.toAnonymous()
if (myIdentity.name == curState.parties[0].name) { if (myIdentity == curState.parties[0]) {
val otherParty = curState.parties[1] val otherParty = serviceHub.identityService.partyFromAnonymous(curState.parties[1])
subFlow(SimmFlow.Requester(otherParty, valuationDate, stateAndRef)) require (otherParty != null) { "Other party must be known by this node" }
subFlow(SimmFlow.Requester(otherParty!!, valuationDate, stateAndRef))
} }
} }
} }

View File

@ -7,7 +7,6 @@ import net.corda.core.contracts.Issued
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.USD import net.corda.core.contracts.USD
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
@ -108,7 +107,7 @@ data class CrossCashState(
it.value.map { it.value.map {
val notifier = it.key val notifier = it.key
" $notifier: [" + it.value.map { " $notifier: [" + it.value.map {
Issued(PartyAndReference(it.first, OpaqueBytes.of(0)), it.second) Issued(PartyAndReference(it.first.toAnonymous(), OpaqueBytes.of(0)), it.second)
}.joinToString(",") + "]" }.joinToString(",") + "]"
}.joinToString("\n") }.joinToString("\n")
}.joinToString("\n") }.joinToString("\n")
@ -126,7 +125,7 @@ val crossCashTest = LoadTest<CrossCashCommand, CrossCashState>(
val quantities = state.nodeVaults[node.info.legalIdentity] ?: mapOf() val quantities = state.nodeVaults[node.info.legalIdentity] ?: mapOf()
val possibleRecipients = nodeMap.keys.toList() val possibleRecipients = nodeMap.keys.toList()
val moves = quantities.map { val moves = quantities.map {
it.value.toDouble() / 1000 to generateMove(it.value, USD, it.key, possibleRecipients) it.value.toDouble() / 1000 to generateMove(it.value, USD, it.key.toAnonymous(), possibleRecipients)
} }
val exits = quantities.mapNotNull { val exits = quantities.mapNotNull {
if (it.key == node.info.legalIdentity) { if (it.key == node.info.legalIdentity) {

View File

@ -79,7 +79,7 @@ val selfIssueTest = LoadTest<SelfIssueCommand, SelfIssueState>(
val state = it.state.data val state = it.state.data
if (state is Cash.State) { if (state is Cash.State) {
val issuer = state.amount.token.issuer.party val issuer = state.amount.token.issuer.party
if (issuer == node.info.legalIdentity) { if (issuer == node.info.legalIdentity as AnonymousParty) {
selfIssueVaults.put(issuer, (selfIssueVaults[issuer] ?: 0L) + state.amount.quantity) selfIssueVaults.put(issuer, (selfIssueVaults[issuer] ?: 0L) + state.amount.quantity)
} }
} }