mirror of
https://github.com/corda/corda.git
synced 2025-02-25 11:03:01 +00:00
Remove use of full parties from contract states
This commit is contained in:
parent
d132d9ed2e
commit
71babc7019
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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>"
|
||||
|
@ -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})"
|
||||
}
|
@ -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 }
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)))
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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? {
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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),
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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()
|
||||
|
@ -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 }
|
||||
|
@ -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())
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user