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
|
* 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"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>"
|
||||||
|
@ -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})"
|
||||||
}
|
}
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)))
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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? {
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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),
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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()
|
||||||
|
@ -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 }
|
||||||
|
@ -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())
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ fun main(args: Array<String>) {
|
|||||||
val eventGenerator = EventGenerator(
|
val eventGenerator = EventGenerator(
|
||||||
parties = listOf(aliceNode.nodeInfo.legalIdentity, bobNode.nodeInfo.legalIdentity),
|
parties = listOf(aliceNode.nodeInfo.legalIdentity, bobNode.nodeInfo.legalIdentity),
|
||||||
notary = notaryNode.nodeInfo.notaryIdentity,
|
notary = notaryNode.nodeInfo.notaryIdentity,
|
||||||
issuers = listOf(issuerNodeGBP.nodeInfo.legalIdentity,issuerNodeUSD.nodeInfo.legalIdentity)
|
issuers = listOf(issuerNodeGBP.nodeInfo.legalIdentity, issuerNodeUSD.nodeInfo.legalIdentity)
|
||||||
)
|
)
|
||||||
val issuerGBPEventGenerator = EventGenerator(
|
val issuerGBPEventGenerator = EventGenerator(
|
||||||
parties = listOf(issuerNodeGBP.nodeInfo.legalIdentity, aliceNode.nodeInfo.legalIdentity, bobNode.nodeInfo.legalIdentity),
|
parties = listOf(issuerNodeGBP.nodeInfo.legalIdentity, aliceNode.nodeInfo.legalIdentity, bobNode.nodeInfo.legalIdentity),
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user