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
* [ContractState]s.
* */
val parties: List<Party>
val parties: List<AnonymousParty>
/**
* 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.
*/
data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) {
constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), 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 */
override fun equals(other: Any?): Boolean = other is AnonymousParty && this.owningKey == other.owningKey
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]
// can put in the key and actual name
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) {
/** 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)
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 : 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 }
}

View File

@ -428,6 +428,7 @@ fun createKryo(k: Kryo = Kryo()): Kryo {
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
// This is required to make all the unit tests pass
register(AnonymousParty::class.java)
register(Party::class.java)
// 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) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Party {
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) {
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
// to have one.
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.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.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>(
var lifecycle: Lifecycle = Lifecycle.NORMAL,
/** Where the debt originates from (obligor) */
val obligor: Party,
val obligor: AnonymousParty,
val template: Terms<P>,
val quantity: Long,
/** The public key of the entity the contract pays to */
val beneficiary: CompositeKey
) : 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 contract = OBLIGATION_PROGRAM_ID
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.
*/
fun generateIssue(tx: TransactionBuilder,
obligor: Party,
obligor: AnonymousParty,
issuanceDef: Terms<P>,
pennies: Long,
beneficiary: CompositeKey,
notary: Party) {
check(tx.inputStates().isEmpty())
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)
}
@ -475,7 +482,7 @@ class Obligation<P> : Contract {
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL })
}
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()
// 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)
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>.`issued by`(party: Party) = copy(obligor = party)
infix fun <T> Obligation.State<T>.`issued by`(party: AnonymousParty) = copy(obligor = party.toAnonymous())
// For Java users:
@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. */
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>
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, 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 partyFromName(name: String): Party? = nameToParties[name]
override fun partyFromAnonymous(party: AnonymousParty): Party? {
return if (party is Party)
party
else
partyFromKey(party.owningKey)
}
override fun partyFromAnonymous(party: AnonymousParty): Party? = partyFromKey(party.owningKey)
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.core.ErrorOr
import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.*
import net.corda.core.flows.FlowException
import net.corda.core.flows.IllegalFlowLogicException
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(WireTransaction::class.java, WireTransactionSerializer)
register(SerializedBytes::class.java, SerializedBytesSerializer)
register(AnonymousParty::class.java)
register(Party::class.java)
register(Array<Any>(0,{}).javaClass)
register(Class::class.java, ClassSerializer)

View File

@ -30,16 +30,20 @@ import java.time.LocalDateTime
object JsonSupport {
interface PartyObjectMapper {
fun partyFromName(partyName: String): Party?
fun partyFromKey(owningKey: CompositeKey): Party?
}
class RpcObjectMapper(val rpc: CordaRPCOps) : PartyObjectMapper, ObjectMapper() {
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(){
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() {
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 {
@ -53,6 +57,8 @@ object JsonSupport {
val cordaModule: Module by lazy {
SimpleModule("core").apply {
addSerializer(AnonymousParty::class.java, AnonymousPartySerializer)
addDeserializer(AnonymousParty::class.java, AnonymousPartyDeserializer)
addSerializer(Party::class.java, PartySerializer)
addDeserializer(Party::class.java, PartyDeserializer)
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>() {
override fun serialize(obj: Party, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeString(obj.name)

View File

@ -2,6 +2,7 @@ package net.corda.irs
import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.Futures
import net.corda.core.crypto.Party
import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo
import net.corda.irs.api.NodeInterestRates
@ -44,7 +45,7 @@ class IRSDemoTest : IntegrationTestCategory {
val nextFixingDates = getFixingDateObservable(nodeA.configuration)
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
nextFixingDates.first { it == null || it > currentDate }
@ -72,9 +73,11 @@ class IRSDemoTest : IntegrationTestCategory {
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 tradeFile = fileContents.replace("tradeXXX", "trade1")
.replace("fixedRatePayerKey", fixedRatePayer.owningKey.toBase58String())
.replace("floatingRatePayerKey", floatingRatePayer.owningKey.toBase58String())
val url = URL("http://$nodeAddr/api/irs/deals")
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.clauses.*
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
@ -304,7 +305,7 @@ class InterestRateSwap() : Contract {
}
open class FixedLeg(
var fixedRatePayer: Party,
var fixedRatePayer: AnonymousParty,
notional: Amount<Currency>,
paymentFrequency: Frequency,
effectiveDate: LocalDate,
@ -343,7 +344,7 @@ class InterestRateSwap() : Contract {
override fun hashCode() = super.hashCode() + 31 * Objects.hash(fixedRatePayer, fixedRate, rollConvention)
// Can't autogenerate as not a data class :-(
fun copy(fixedRatePayer: Party = this.fixedRatePayer,
fun copy(fixedRatePayer: AnonymousParty = this.fixedRatePayer,
notional: Amount<Currency> = this.notional,
paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate,
@ -365,7 +366,7 @@ class InterestRateSwap() : Contract {
}
open class FloatingLeg(
var floatingRatePayer: Party,
var floatingRatePayer: AnonymousParty,
notional: Amount<Currency>,
paymentFrequency: Frequency,
effectiveDate: LocalDate,
@ -423,7 +424,7 @@ class InterestRateSwap() : Contract {
index, indexSource, indexTenor)
fun copy(floatingRatePayer: Party = this.floatingRatePayer,
fun copy(floatingRatePayer: AnonymousParty = this.floatingRatePayer,
notional: Amount<Currency> = this.notional,
paymentFrequency: Frequency = this.paymentFrequency,
effectiveDate: LocalDate = this.effectiveDate,
@ -672,7 +673,7 @@ class InterestRateSwap() : Contract {
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)
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 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.node.CordaPluginRegistry
import net.corda.core.node.PluginServiceHub
@ -65,7 +65,7 @@ object AutoOfferFlow {
require(serviceHub.networkMapCache.notaryNodes.isNotEmpty()) { "No notary nodes registered" }
val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity
// 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
val myKey = serviceHub.legalIdentityKey
val instigator = Instigator(
@ -78,8 +78,8 @@ object AutoOfferFlow {
return stx
}
private fun notUs(parties: List<Party>): List<Party> {
val notUsParties: MutableList<Party> = arrayListOf()
private fun <T: AnonymousParty> notUs(parties: List<T>): List<T> {
val notUsParties: MutableList<T> = arrayListOf()
for (party in parties) {
if (serviceHub.myInfo.legalIdentity != party) {
notUsParties.add(party)

View File

@ -151,8 +151,9 @@ object FixingFlow {
val myKey = serviceHub.myInfo.legalIdentity.owningKey
if (parties[0].owningKey == myKey) {
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
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.
// 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"))
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity
irs.fixedLeg.fixedRatePayer = node1.info.legalIdentity.toAnonymous()
irs.floatingLeg.floatingRatePayer = node2.info.legalIdentity.toAnonymous()
@Suppress("UNCHECKED_CAST")
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> {
return bankLocations.map {
network.createNode(networkMap.info.address, start = false, nodeFactory = this) as SimulatedNode
return bankLocations.mapIndexed { i, location ->
// 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": {
"fixedRatePayer": "Bank A",
"fixedRatePayer": "fixedRatePayerKey",
"notional": {
"quantity": 2500000000,
"token": "EUR"
@ -25,7 +25,7 @@
"interestPeriodAdjustment": "Adjusted"
},
"floatingLeg": {
"floatingRatePayer": "Bank B",
"floatingRatePayer": "floatingRatePayerKey",
"notional": {
"quantity": 2500000000,
"token": "EUR"

View File

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

View File

@ -20,7 +20,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
1 -> {
val fixedLeg = InterestRateSwap.FixedLeg(
fixedRatePayer = MEGA_CORP,
fixedRatePayer = MEGA_CORP.toAnonymous(),
notional = 15900000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2016, 3, 10),
@ -39,7 +39,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
)
val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP,
floatingRatePayer = MINI_CORP.toAnonymous(),
notional = 15900000.DOLLARS,
paymentFrequency = Frequency.Quarterly,
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
val fixedLeg = InterestRateSwap.FixedLeg(
fixedRatePayer = MEGA_CORP,
fixedRatePayer = MEGA_CORP.toAnonymous(),
notional = 25000000.DOLLARS,
paymentFrequency = Frequency.SemiAnnual,
effectiveDate = LocalDate.of(2015, 3, 10),
@ -129,7 +129,7 @@ fun createDummyIRS(irsSelect: Int): InterestRateSwap.State {
)
val floatingLeg = InterestRateSwap.FloatingLeg(
floatingRatePayer = MINI_CORP,
floatingRatePayer = MINI_CORP.toAnonymous(),
notional = 25000000.DOLLARS,
paymentFrequency = Frequency.Quarterly,
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.StateAndRef
import net.corda.core.contracts.filterStatesOfType
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.getOrThrow
@ -33,7 +34,7 @@ class PortfolioApi(val rpc: CordaRPCOps) {
private val ownParty: Party get() = rpc.nodeIdentity().legalIdentity
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 } }
}
@ -220,7 +221,7 @@ class PortfolioApi(val rpc: CordaRPCOps) {
return withParty(partyName) { party ->
withPortfolio(party) { state ->
if (state.valuation != null) {
val isValuer = state.valuer.name == ownParty.name
val isValuer = state.valuer as AnonymousParty == ownParty
val rawMtm = state.valuation.presentValues.map {
it.value.amounts.first().amount
}.reduce { a, b -> a + b }

View File

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

View File

@ -27,7 +27,7 @@ data class SwapDataModel(
*/
fun toData(buyer: Party, seller: Party): 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,
presentValue: MultiCurrencyAmount? = null,
IM: InitialMarginTriple? = null): SwapDataView {
val isBuyer = viewingParty.name == buyer.second
val isBuyer = viewingParty.owningKey == buyer.second
val trade = if (isBuyer) toFixedLeg() else toFloatingLeg()
val leg = getLegForParty(viewingParty)
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.TransactionType
import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.transactions.TransactionBuilder
@ -15,12 +16,12 @@ import java.security.PublicKey
* TODO: Merge with the existing demo IRS code.
*/
data class IRSState(val swap: SwapData,
val buyer: Party,
val seller: Party,
val buyer: AnonymousParty,
val seller: AnonymousParty,
override val contract: OGTrade,
override val linearId: UniqueIdentifier = UniqueIdentifier(swap.id.first + swap.id.second)) : DealState {
override val ref: String = linearId.externalId!! // Same as the constructor for UniqueIdentified
override val parties: List<Party> get() = listOf(buyer, seller)
override val parties: List<AnonymousParty> get() = listOf(buyer, seller)
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return parties.flatMap { it.owningKey.keys }.intersect(ourKeys).isNotEmpty()

View File

@ -1,6 +1,7 @@
package net.corda.vega.contracts
import net.corda.core.contracts.*
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogicRefFactory
@ -17,16 +18,16 @@ import java.time.temporal.ChronoUnit
*/
data class PortfolioState(val portfolio: List<StateRef>,
override val contract: PortfolioSwap,
private val _parties: Pair<Party, Party>,
private val _parties: Pair<AnonymousParty, AnonymousParty>,
val valuationDate: LocalDate,
val valuation: PortfolioValuation? = null,
override val linearId: UniqueIdentifier = UniqueIdentifier())
: RevisionedState<PortfolioState.Update>, SchedulableState, DealState {
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()
val valuer: Party get() = parties[0]
val valuer: AnonymousParty get() = parties[0]
override val participants: List<CompositeKey>
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.type.FixedIborSwapConvention
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 java.math.BigDecimal
import java.time.LocalDate
@ -36,8 +38,8 @@ data class FloatingLeg(val _notional: BigDecimal, override val notional: BigDeci
*/
data class SwapData(
val id: Pair<String, String>,
val buyer: Pair<String, String>,
val seller: Pair<String, String>,
val buyer: Pair<String, CompositeKey>,
val seller: Pair<String, CompositeKey>,
val description: String,
val tradeDate: LocalDate,
val convention: String,
@ -46,8 +48,8 @@ data class SwapData(
val notional: BigDecimal,
val fixedRate: BigDecimal) {
fun getLegForParty(party: Party): Leg {
return if (party.name == buyer.second) FixedLeg(notional) else FloatingLeg(notional)
fun getLegForParty(party: AnonymousParty): Leg {
return if (party == buyer.second) FixedLeg(notional) else FloatingLeg(notional)
}
fun toFixedLeg(): SwapTrade {
@ -58,11 +60,11 @@ data class SwapData(
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()
.id(StandardId.of(id.first, id.second))
.addAttribute(TradeAttributeType.DESCRIPTION, description)
.counterparty(StandardId.of(party.first, party.second))
.counterparty(StandardId.of(party.first, party.second.toBase58String()))
.build()
// 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())

View File

@ -22,12 +22,12 @@ object IRSTradeFlow {
val notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity
val myIdentity = serviceHub.myInfo.legalIdentity
val (buyer, seller) =
if (swap.buyer.second == myIdentity.name) {
if (swap.buyer.second == myIdentity.owningKey) {
Pair(myIdentity, otherParty)
} else {
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")
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 net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.messaging.Ack
@ -59,7 +60,7 @@ object SimmFlow {
@Suspendable
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" }
notary = serviceHub.networkMapCache.notaryNodes.first().notaryIdentity
myIdentity = serviceHub.myInfo.legalIdentity
@ -80,7 +81,7 @@ object SimmFlow {
@Suspendable
private fun agreePortfolio(portfolio: 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)
send(otherParty, OfferMessage(notary, portfolioState, existing?.ref, valuationDate))
@ -102,8 +103,9 @@ object SimmFlow {
logger.info("Agreeing valuations")
val state = stateRef.state.data
val portfolio = state.portfolio.toStateAndRef<IRSState>(serviceHub).toPortfolio()
val valuer = state.valuer
val valuation = agreeValuation(portfolio, valuationDate, valuer)
val valuer = serviceHub.identityService.partyFromAnonymous(state.valuer)
require(valuer != null) { "Valuer party must be known to this node" }
val valuation = agreeValuation(portfolio, valuationDate, valuer!!)
val update = PortfolioState.Update(valuation = valuation)
return subFlow(StateRevisionFlow.Requester(stateRef, update), shareParentSessions = true).state.data
}
@ -213,6 +215,13 @@ object SimmFlow {
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
* 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 {
val stateAndRef = serviceHub.vaultService.linearHeadsOfType<PortfolioState>().values.first { it.ref == curStateRef }
val curState = stateAndRef.state.data
val myIdentity = serviceHub.myInfo.legalIdentity
if (myIdentity.name == curState.parties[0].name) {
val otherParty = curState.parties[1]
subFlow(SimmFlow.Requester(otherParty, valuationDate, stateAndRef))
val myIdentity = serviceHub.myInfo.legalIdentity.toAnonymous()
if (myIdentity == curState.parties[0]) {
val otherParty = serviceHub.identityService.partyFromAnonymous(curState.parties[1])
require (otherParty != null) { "Other party must be known by this node" }
subFlow(SimmFlow.Requester(otherParty!!, valuationDate, stateAndRef))
}
}
}

View File

@ -169,7 +169,7 @@ fun main(args: Array<String>) {
val eventGenerator = EventGenerator(
parties = listOf(aliceNode.nodeInfo.legalIdentity, bobNode.nodeInfo.legalIdentity),
notary = notaryNode.nodeInfo.notaryIdentity,
issuers = listOf(issuerNodeGBP.nodeInfo.legalIdentity,issuerNodeUSD.nodeInfo.legalIdentity)
issuers = listOf(issuerNodeGBP.nodeInfo.legalIdentity, issuerNodeUSD.nodeInfo.legalIdentity)
)
val issuerGBPEventGenerator = EventGenerator(
parties = listOf(issuerNodeGBP.nodeInfo.legalIdentity, aliceNode.nodeInfo.legalIdentity, bobNode.nodeInfo.legalIdentity),

View File

@ -7,7 +7,6 @@ import net.corda.core.contracts.Issued
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.USD
import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.Party
import net.corda.core.flows.FlowException
import net.corda.core.getOrThrow
import net.corda.core.messaging.startFlow
@ -108,7 +107,7 @@ data class CrossCashState(
it.value.map {
val notifier = it.key
" $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("\n")
}.joinToString("\n")
@ -126,7 +125,7 @@ val crossCashTest = LoadTest<CrossCashCommand, CrossCashState>(
val quantities = state.nodeVaults[node.info.legalIdentity] ?: mapOf()
val possibleRecipients = nodeMap.keys.toList()
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 {
if (it.key == node.info.legalIdentity) {

View File

@ -79,7 +79,7 @@ val selfIssueTest = LoadTest<SelfIssueCommand, SelfIssueState>(
val state = it.state.data
if (state is Cash.State) {
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)
}
}