Refactor of CompositeKeys to implement PublicKey interface. (#433)

* Make CompositeKey implement PublicKey

The initial implementation of composite keys as their own distinct class separate from PublicKey
means that the keys cannot be used on standard classes such as Certificate. This work is a beginning
to modifying CompositeKey to being a PublicKey implementation, although significant further work
is required to integrate this properly with the standard Java APIs, especially around verifying
signatures using the new key type.

* First stage of making CompositeKey implement PublicKey interface. Revert to using PublicKey everywhere we expect a key.

* Move algorithm and format into companion object (#432)

Move algorithm and format into companion object so that they can be referenced from other
classes (i.e. the upcoming signature class).

* Add simple invariants to construction of CompositeKey.
Builder emits CompositeKeys in simplified normalised form. Forbid keys with single child node, force ordering on children and forbid duplicates on the same level. It's not full semantical normalisation.

* Make constructor of CompositeKey private, move NodeWeight inside the class.
Add utility function for Kryo deserialization to read list with length constraints.
This commit is contained in:
kasiastreich 2017-04-12 11:13:20 +01:00 committed by GitHub
parent cb84f7b707
commit 36d5d0d7b2
124 changed files with 707 additions and 606 deletions

View File

@ -18,6 +18,7 @@ import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey
import java.util.* import java.util.*
/** /**
@ -32,22 +33,22 @@ object JacksonSupport {
interface PartyObjectMapper { interface PartyObjectMapper {
fun partyFromName(partyName: String): Party? fun partyFromName(partyName: String): Party?
fun partyFromKey(owningKey: CompositeKey): Party? fun partyFromKey(owningKey: PublicKey): Party?
} }
class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) { class RpcObjectMapper(val rpc: CordaRPCOps, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
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) override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey)
} }
class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) { class IdentityObjectMapper(val identityService: IdentityService, factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
override fun partyFromName(partyName: String): Party? = identityService.partyFromName(partyName) override fun partyFromName(partyName: String): Party? = identityService.partyFromName(partyName)
override fun partyFromKey(owningKey: CompositeKey): Party? = identityService.partyFromKey(owningKey) override fun partyFromKey(owningKey: PublicKey): Party? = identityService.partyFromKey(owningKey)
} }
class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) { class NoPartyObjectMapper(factory: JsonFactory) : PartyObjectMapper, ObjectMapper(factory) {
override fun partyFromName(partyName: String): Party? = throw UnsupportedOperationException() override fun partyFromName(partyName: String): Party? = throw UnsupportedOperationException()
override fun partyFromKey(owningKey: CompositeKey): Party? = throw UnsupportedOperationException() override fun partyFromKey(owningKey: PublicKey): Party? = throw UnsupportedOperationException()
} }
val cordaModule: Module by lazy { val cordaModule: Module by lazy {
@ -128,7 +129,7 @@ object JacksonSupport {
} }
// TODO this needs to use some industry identifier(s) instead of these keys // TODO this needs to use some industry identifier(s) instead of these keys
val key = CompositeKey.parseFromBase58(parser.text) val key = parsePublicKeyBase58(parser.text)
return AnonymousParty(key) return AnonymousParty(key)
} }
} }
@ -214,7 +215,7 @@ object JacksonSupport {
object PublicKeyDeserializer : JsonDeserializer<EdDSAPublicKey>() { object PublicKeyDeserializer : JsonDeserializer<EdDSAPublicKey>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): EdDSAPublicKey { override fun deserialize(parser: JsonParser, context: DeserializationContext): EdDSAPublicKey {
return try { return try {
parsePublicKeyBase58(parser.text) parsePublicKeyBase58(parser.text) as EdDSAPublicKey
} catch (e: Exception) { } catch (e: Exception) {
throw JsonParseException(parser, "Invalid public key ${parser.text}: ${e.message}") throw JsonParseException(parser, "Invalid public key ${parser.text}: ${e.message}")
} }
@ -230,7 +231,7 @@ object JacksonSupport {
object CompositeKeyDeserializer : JsonDeserializer<CompositeKey>() { object CompositeKeyDeserializer : JsonDeserializer<CompositeKey>() {
override fun deserialize(parser: JsonParser, context: DeserializationContext): CompositeKey { override fun deserialize(parser: JsonParser, context: DeserializationContext): CompositeKey {
return try { return try {
CompositeKey.parseFromBase58(parser.text) parsePublicKeyBase58(parser.text) as CompositeKey
} catch (e: Exception) { } catch (e: Exception) {
throw JsonParseException(parser, "Invalid composite key ${parser.text}: ${e.message}") throw JsonParseException(parser, "Invalid composite key ${parser.text}: ${e.message}")
} }

View File

@ -6,6 +6,8 @@ import net.corda.core.bufferUntilSubscribed
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.DOLLARS import net.corda.core.contracts.DOLLARS
import net.corda.core.contracts.USD import net.corda.core.contracts.USD
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.keys
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
@ -151,25 +153,25 @@ class NodeMonitorModelTest : DriverBasedTest() {
transactions.expectEvents { transactions.expectEvents {
sequence( sequence(
// ISSUE // ISSUE
expect { tx -> expect { stx ->
require(tx.tx.inputs.isEmpty()) require(stx.tx.inputs.isEmpty())
require(tx.tx.outputs.size == 1) require(stx.tx.outputs.size == 1)
val signaturePubKeys = tx.sigs.map { it.by }.toSet() val signaturePubKeys = stx.sigs.map { it.by }.toSet()
// Only Alice signed // Only Alice signed
val aliceKey = aliceNode.legalIdentity.owningKey val aliceKey = aliceNode.legalIdentity.owningKey
require(signaturePubKeys.size <= aliceKey.keys.size) require(signaturePubKeys.size <= aliceKey.keys.size)
require(aliceKey.isFulfilledBy(signaturePubKeys)) require(aliceKey.isFulfilledBy(signaturePubKeys))
issueTx = tx issueTx = stx
}, },
// MOVE // MOVE
expect { tx -> expect { stx ->
require(tx.tx.inputs.size == 1) require(stx.tx.inputs.size == 1)
require(tx.tx.outputs.size == 1) require(stx.tx.outputs.size == 1)
val signaturePubKeys = tx.sigs.map { it.by }.toSet() val signaturePubKeys = stx.sigs.map { it.by }.toSet()
// Alice and Notary signed // Alice and Notary signed
require(aliceNode.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys)) require(aliceNode.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys))
require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys)) require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys))
moveTx = tx moveTx = stx
} }
) )
} }

View File

@ -7,7 +7,7 @@ import net.corda.client.jfx.utils.firstOrDefault
import net.corda.client.jfx.utils.firstOrNullObservable import net.corda.client.jfx.utils.firstOrNullObservable
import net.corda.client.jfx.utils.fold import net.corda.client.jfx.utils.fold
import net.corda.client.jfx.utils.map import net.corda.client.jfx.utils.map
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.keys
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.NetworkMapCache.MapChange
import java.security.PublicKey import java.security.PublicKey
@ -39,10 +39,6 @@ class NetworkIdentityModel {
} }
// TODO: Use Identity Service in service hub instead? // TODO: Use Identity Service in service hub instead?
fun lookup(compositeKey: CompositeKey): ObservableValue<NodeInfo?> = parties.firstOrDefault(notaries.firstOrNullObservable { it.notaryIdentity.owningKey == compositeKey }) {
it.legalIdentity.owningKey == compositeKey
}
fun lookup(publicKey: PublicKey): ObservableValue<NodeInfo?> = parties.firstOrDefault(notaries.firstOrNullObservable { it.notaryIdentity.owningKey.keys.any { it == publicKey } }) { fun lookup(publicKey: PublicKey): ObservableValue<NodeInfo?> = parties.firstOrDefault(notaries.firstOrNullObservable { it.notaryIdentity.owningKey.keys.any { it == publicKey } }) {
it.legalIdentity.owningKey.keys.any { it == publicKey } it.legalIdentity.owningKey.keys.any { it == publicKey }
} }

View File

@ -2,8 +2,8 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.security.PublicKey
import java.math.BigDecimal import java.math.BigDecimal
import java.util.* import java.util.*
@ -69,7 +69,7 @@ inline fun <R> requireThat(body: Requirements.() -> R) = Requirements.body()
// TODO: Provide a version of select that interops with Java // TODO: Provide a version of select that interops with Java
/** Filters the command list by type, party and public key all at once. */ /** Filters the command list by type, party and public key all at once. */
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signer: CompositeKey? = null, inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signer: PublicKey? = null,
party: Party? = null) = party: Party? = null) =
filter { it.value is T }. filter { it.value is T }.
filter { if (signer == null) true else signer in it.signers }. filter { if (signer == null) true else signer in it.signers }.
@ -79,7 +79,7 @@ inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>
// TODO: Provide a version of select that interops with Java // TODO: Provide a version of select that interops with Java
/** Filters the command list by type, parties and public keys all at once. */ /** Filters the command list by type, parties and public keys all at once. */
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signers: Collection<CompositeKey>?, inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signers: Collection<PublicKey>?,
parties: Collection<Party>?) = parties: Collection<Party>?) =
filter { it.value is T }. filter { it.value is T }.
filter { if (signers == null) true else it.signers.containsAll(signers) }. filter { if (signers == null) true else it.signers.containsAll(signers) }.

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts package net.corda.core.contracts
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
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
@ -14,12 +14,12 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
val magicNumber: Int val magicNumber: Int
} }
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: CompositeKey) : OwnableState, State { data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKey) : OwnableState, State {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
} }
/** /**
@ -28,9 +28,9 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
* in a different field, however this is a good example of a contract with multiple states. * in a different field, however this is a good example of a contract with multiple states.
*/ */
data class MultiOwnerState(override val magicNumber: Int = 0, data class MultiOwnerState(override val magicNumber: Int = 0,
val owners: List<CompositeKey>) : ContractState, State { val owners: List<PublicKey>) : ContractState, State {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<CompositeKey> get() = owners override val participants: List<PublicKey> get() = owners
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -55,8 +55,8 @@ data class DummyContract(override val legalContractReference: SecureHash = Secur
} }
} }
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: CompositeKey) = move(listOf(prior), newOwner) fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: CompositeKey): TransactionBuilder { fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder {
require(priors.isNotEmpty()) require(priors.isNotEmpty())
val priorState = priors[0].state.data val priorState = priors[0].state.data
val (cmd, state) = priorState.withNewOwner(newOwner) val (cmd, state) = priorState.withNewOwner(newOwner)

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.flows.ContractUpgradeFlow import net.corda.flows.ContractUpgradeFlow
import java.security.PublicKey
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
val DUMMY_V2_PROGRAM_ID = DummyContractV2() val DUMMY_V2_PROGRAM_ID = DummyContractV2()
@ -11,12 +11,13 @@ val DUMMY_V2_PROGRAM_ID = DummyContractV2()
/** /**
* Dummy contract state for testing of the upgrade process. * Dummy contract state for testing of the upgrade process.
*/ */
// DOCSTART 1
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> { class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
override val legacyContract = DummyContract::class.java override val legacyContract = DummyContract::class.java
data class State(val magicNumber: Int = 0, val owners: List<CompositeKey>) : ContractState { data class State(val magicNumber: Int = 0, val owners: List<PublicKey>) : ContractState {
override val contract = DUMMY_V2_PROGRAM_ID override val contract = DUMMY_V2_PROGRAM_ID
override val participants: List<CompositeKey> = owners override val participants: List<PublicKey> = owners
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -35,7 +36,7 @@ class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.St
// The "empty contract" // The "empty contract"
override val legalContractReference: SecureHash = SecureHash.sha256("") override val legalContractReference: SecureHash = SecureHash.sha256("")
// DOCEND 1
/** /**
* Generate an upgrade transaction from [DummyContract]. * Generate an upgrade transaction from [DummyContract].
* *
@ -43,7 +44,7 @@ class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.St
* *
* @return a pair of wire transaction, and a set of those who should sign the transaction for it to be valid. * @return a pair of wire transaction, and a set of those who should sign the transaction for it to be valid.
*/ */
fun generateUpgradeFromV1(vararg states: StateAndRef<DummyContract.State>): Pair<WireTransaction, Set<CompositeKey>> { fun generateUpgradeFromV1(vararg states: StateAndRef<DummyContract.State>): Pair<WireTransaction, Set<PublicKey>> {
val notary = states.map { it.state.notary }.single() val notary = states.map { it.state.notary }.single()
require(states.isNotEmpty()) require(states.isNotEmpty())

View File

@ -1,12 +1,12 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey import java.security.PublicKey
/** /**
* Dummy state for use in testing. Not part of any contract, not even the [DummyContract]. * Dummy state for use in testing. Not part of any contract, not even the [DummyContract].
*/ */
data class DummyState(val magicNumber: Int = 0) : ContractState { data class DummyState(val magicNumber: Int = 0) : ContractState {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = emptyList() get() = emptyList()
} }

View File

@ -1,7 +1,7 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import java.security.PublicKey
class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing") class InsufficientBalanceException(val amountMissing: Amount<*>) : FlowException("Insufficient balance, missing $amountMissing")
@ -25,11 +25,11 @@ interface FungibleAsset<T : Any> : OwnableState {
* There must be an ExitCommand signed by these keys to destroy the amount. While all states require their * There must be an ExitCommand signed by these keys to destroy the amount. While all states require their
* owner to sign, some (i.e. cash) also require the issuer. * owner to sign, some (i.e. cash) also require the issuer.
*/ */
val exitKeys: Collection<CompositeKey> val exitKeys: Collection<PublicKey>
/** There must be a MoveCommand signed by this key to claim the amount */ /** There must be a MoveCommand signed by this key to claim the amount */
override val owner: CompositeKey override val owner: PublicKey
fun move(newAmount: Amount<Issued<T>>, newOwner: CompositeKey): FungibleAsset<T> fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKey): FungibleAsset<T>
// Just for grouping // Just for grouping
interface Commands : CommandData { interface Commands : CommandData {

View File

@ -2,7 +2,6 @@ package net.corda.core.contracts
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.crypto.AnonymousParty 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 net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogicRef import net.corda.core.flows.FlowLogicRef
@ -116,7 +115,7 @@ interface ContractState {
* The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants * The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants
* list should just contain the owner. * list should just contain the owner.
*/ */
val participants: List<CompositeKey> val participants: List<PublicKey>
} }
/** /**
@ -189,10 +188,10 @@ fun <T : Any> Amount<Issued<T>>.withoutIssuer(): Amount<T> = Amount(quantity, to
*/ */
interface OwnableState : ContractState { interface OwnableState : ContractState {
/** There must be a MoveCommand signed by this key to claim the amount */ /** There must be a MoveCommand signed by this key to claim the amount */
val owner: CompositeKey val owner: PublicKey
/** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */ /** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */
fun withNewOwner(newOwner: CompositeKey): Pair<CommandData, OwnableState> fun withNewOwner(newOwner: PublicKey): Pair<CommandData, OwnableState>
} }
/** Something which is scheduled to happen at a point in time */ /** Something which is scheduled to happen at a point in time */
@ -237,7 +236,7 @@ interface LinearState : ContractState {
/** /**
* True if this should be tracked by our vault(s). * True if this should be tracked by our vault(s).
* */ */
fun isRelevant(ourKeys: Set<PublicKey>): Boolean fun isRelevant(ourKeys: Set<PublicKey>): Boolean
/** /**
@ -376,12 +375,12 @@ abstract class TypeOnlyCommandData : CommandData {
/** Command data/content plus pubkey pair: the signature is stored at the end of the serialized bytes */ /** Command data/content plus pubkey pair: the signature is stored at the end of the serialized bytes */
@CordaSerializable @CordaSerializable
data class Command(val value: CommandData, val signers: List<CompositeKey>) { data class Command(val value: CommandData, val signers: List<PublicKey>) {
init { init {
require(signers.isNotEmpty()) require(signers.isNotEmpty())
} }
constructor(data: CommandData, key: CompositeKey) : this(data, listOf(key)) constructor(data: CommandData, key: PublicKey) : this(data, listOf(key))
private fun commandDataToString() = value.toString().let { if (it.contains("@")) it.replace('$', '.').split("@")[0] else it } private fun commandDataToString() = value.toString().let { if (it.contains("@")) it.replace('$', '.').split("@")[0] else it }
override fun toString() = "${commandDataToString()} with pubkeys ${signers.joinToString()}" override fun toString() = "${commandDataToString()} with pubkeys ${signers.joinToString()}"
@ -415,7 +414,7 @@ data class UpgradeCommand(val upgradedContractClass: Class<out UpgradedContract<
/** Wraps an object that was signed by a public key, which may be a well known/recognised institutional key. */ /** Wraps an object that was signed by a public key, which may be a well known/recognised institutional key. */
@CordaSerializable @CordaSerializable
data class AuthenticatedObject<out T : Any>( data class AuthenticatedObject<out T : Any>(
val signers: List<CompositeKey>, val signers: List<PublicKey>,
/** If any public keys were recognised, the looked up institutions are available here */ /** If any public keys were recognised, the looked up institutions are available here */
val signingParties: List<Party>, val signingParties: List<Party>,
val value: T val value: T

View File

@ -1,10 +1,10 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
/** Defines transaction build & validation logic for a specific transaction type */ /** Defines transaction build & validation logic for a specific transaction type */
@CordaSerializable @CordaSerializable
@ -27,7 +27,7 @@ sealed class TransactionType {
} }
/** Check that the list of signers includes all the necessary keys */ /** Check that the list of signers includes all the necessary keys */
fun verifySigners(tx: LedgerTransaction): Set<CompositeKey> { fun verifySigners(tx: LedgerTransaction): Set<PublicKey> {
val notaryKey = tx.inputs.map { it.state.notary.owningKey }.toSet() val notaryKey = tx.inputs.map { it.state.notary.owningKey }.toSet()
if (notaryKey.size > 1) throw TransactionVerificationException.MoreThanOneNotary(tx) if (notaryKey.size > 1) throw TransactionVerificationException.MoreThanOneNotary(tx)
@ -54,7 +54,7 @@ sealed class TransactionType {
* Return the list of public keys that that require signatures for the transaction type. * Return the list of public keys that that require signatures for the transaction type.
* Note: the notary key is checked separately for all transactions and need not be included. * Note: the notary key is checked separately for all transactions and need not be included.
*/ */
abstract fun getRequiredSigners(tx: LedgerTransaction): Set<CompositeKey> abstract fun getRequiredSigners(tx: LedgerTransaction): Set<PublicKey>
/** Implement type specific transaction validation logic */ /** Implement type specific transaction validation logic */
abstract fun verifyTransaction(tx: LedgerTransaction) abstract fun verifyTransaction(tx: LedgerTransaction)

View File

@ -1,11 +1,11 @@
package net.corda.core.contracts package net.corda.core.contracts
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
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import java.security.PublicKey
import java.util.* import java.util.*
// TODO: Consider moving this out of the core module and providing a different way for unit tests to test contracts. // TODO: Consider moving this out of the core module and providing a different way for unit tests to test contracts.
@ -101,7 +101,7 @@ class TransactionConflictException(val conflictRef: StateRef, val tx1: LedgerTra
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : FlowException(cause) { sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : FlowException(cause) {
class ContractRejection(tx: LedgerTransaction, val contract: Contract, cause: Throwable?) : TransactionVerificationException(tx, cause) class ContractRejection(tx: LedgerTransaction, val contract: Contract, cause: Throwable?) : TransactionVerificationException(tx, cause)
class MoreThanOneNotary(tx: LedgerTransaction) : TransactionVerificationException(tx, null) class MoreThanOneNotary(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
class SignersMissing(tx: LedgerTransaction, val missing: List<CompositeKey>) : TransactionVerificationException(tx, null) { class SignersMissing(tx: LedgerTransaction, val missing: List<PublicKey>) : TransactionVerificationException(tx, null) {
override fun toString(): String = "Signers missing: ${missing.joinToString()}" override fun toString(): String = "Signers missing: ${missing.joinToString()}"
} }

View File

@ -10,10 +10,7 @@ import java.security.PublicKey
* the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case. * the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case.
*/ */
@CordaSerializable @CordaSerializable
abstract class AbstractParty(val owningKey: CompositeKey) { abstract class AbstractParty(val owningKey: PublicKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
constructor(owningKey: PublicKey) : this(owningKey.composite)
/** 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 AbstractParty && this.owningKey == other.owningKey override fun equals(other: Any?): Boolean = other is AbstractParty && this.owningKey == other.owningKey

View File

@ -8,10 +8,7 @@ import java.security.PublicKey
* The [AnonymousParty] class contains enough information to uniquely identify a [Party] while excluding private * The [AnonymousParty] class contains enough information to uniquely identify a [Party] while excluding private
* information such as name. It is intended to represent a party on the distributed ledger. * information such as name. It is intended to represent a party on the distributed ledger.
*/ */
class AnonymousParty(owningKey: CompositeKey) : AbstractParty(owningKey) { class AnonymousParty(owningKey: PublicKey) : AbstractParty(owningKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
constructor(owningKey: PublicKey) : this(owningKey.composite)
// Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party] // Use the key as the bulk of the toString(), but include a human readable identifier as well, so that [Party]
// can put in the key and actual name // can put in the key and actual name
override fun toString() = "${owningKey.toBase58String()} <Anonymous>" override fun toString() = "${owningKey.toBase58String()} <Anonymous>"

View File

@ -1,118 +1,146 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.crypto.CompositeKey.Leaf
import net.corda.core.crypto.CompositeKey.Node
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import java.security.PublicKey import java.security.PublicKey
/** /**
* A tree data structure that enables the representation of composite public keys. * A tree data structure that enables the representation of composite public keys.
* Notice that with that implementation CompositeKey extends PublicKey. Leaves are represented by single public keys.
* *
* In the simplest case it may just contain a single node encapsulating a [PublicKey] a [Leaf]. * For complex scenarios, such as *"Both Alice and Bob need to sign to consume a state S"*, we can represent
* * the requirement by creating a tree with a root [CompositeKey], and Alice and Bob as children.
* For more complex scenarios, such as *"Both Alice and Bob need to sign to consume a state S"*, we can represent
* the requirement by creating a tree with a root [Node], and Alice and Bob as children [Leaf]s.
* The root node would specify *weights* for each of its children and a *threshold* the minimum total weight required * The root node would specify *weights* for each of its children and a *threshold* the minimum total weight required
* (e.g. the minimum number of child signatures required) to satisfy the tree signature requirement. * (e.g. the minimum number of child signatures required) to satisfy the tree signature requirement.
* *
* Using these constructs we can express e.g. 1 of N (OR) or N of N (AND) signature requirements. By nesting we can * Using these constructs we can express e.g. 1 of N (OR) or N of N (AND) signature requirements. By nesting we can
* create multi-level requirements such as *"either the CEO or 3 of 5 of his assistants need to sign"*. * create multi-level requirements such as *"either the CEO or 3 of 5 of his assistants need to sign"*.
*
* [CompositeKey] maintains a list of [NodeAndWeight]s which holds child subtree with associated weight carried by child node signatures.
*
* The [threshold] specifies the minimum total weight required (in the simple case the minimum number of child
* signatures required) to satisfy the sub-tree rooted at this node.
*/ */
@CordaSerializable @CordaSerializable
sealed class CompositeKey { class CompositeKey private constructor (val threshold: Int,
/** Checks whether [keys] match a sufficient amount of leaf nodes */ children: List<NodeAndWeight>) : PublicKey {
abstract fun isFulfilledBy(keys: Iterable<PublicKey>): Boolean val children = children.sorted()
init {
fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key)) require (children.size == children.toSet().size) { "Trying to construct CompositeKey with duplicated child nodes." }
// If we want PublicKey we only keep one key, otherwise it will lead to semantically equivalent trees but having different structures.
/** Returns all [PublicKey]s contained within the tree leaves */ require(children.size > 1) { "Cannot construct CompositeKey with only one child node." }
abstract val keys: Set<PublicKey> }
/** Checks whether any of the given [keys] matches a leaf on the tree */
fun containsAny(otherKeys: Iterable<PublicKey>) = keys.intersect(otherKeys).isNotEmpty()
/** /**
* This is generated by serializing the composite key with Kryo, and encoding the resulting bytes in base58. * Holds node - weight pairs for a CompositeKey. Ordered first by weight, then by node's hashCode.
* A custom serialization format is being used.
*
* TODO: follow the crypto-conditions ASN.1 spec, some changes are needed to be compatible with the condition
* structure, e.g. mapping a PublicKey to a condition with the specific feature (ED25519).
*/ */
fun toBase58String(): String = Base58.encode(this.serialize().bytes) @CordaSerializable
data class NodeAndWeight(val node: PublicKey, val weight: Int): Comparable<NodeAndWeight> {
override fun compareTo(other: NodeAndWeight): Int {
if (weight == other.weight) {
return node.hashCode().compareTo(other.node.hashCode())
}
else return weight.compareTo(other.weight)
}
}
companion object { companion object {
fun parseFromBase58(encoded: String) = Base58.decode(encoded).deserialize<CompositeKey>() // TODO: Get the design standardised and from there define a recognised name
} val ALGORITHM = "X-Corda-CompositeKey"
// TODO: We should be using a well defined format.
/** The leaf node of the tree a wrapper around a [PublicKey] primitive */ val FORMAT = "X-Corda-Kryo"
data class Leaf(val publicKey: PublicKey) : CompositeKey() {
override fun isFulfilledBy(keys: Iterable<PublicKey>) = publicKey in keys
override val keys: Set<PublicKey> get() = setOf(publicKey)
override fun toString() = publicKey.toStringShort()
} }
/** /**
* Represents a node in the key tree. It maintains a list of child nodes sub-trees, and associated * Takes single PublicKey and checks if CompositeKey requirements hold for that key.
* [weights] carried by child node signatures.
*
* The [threshold] specifies the minimum total weight required (in the simple case the minimum number of child
* signatures required) to satisfy the sub-tree rooted at this node.
*/ */
data class Node(val threshold: Int, val children: List<CompositeKey>, val weights: List<Int>) : CompositeKey() { fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key))
override fun isFulfilledBy(keys: Iterable<PublicKey>): Boolean {
val totalWeight = children.mapIndexed { i, childNode ->
if (childNode.isFulfilledBy(keys)) weights[i] else 0
}.sum()
return totalWeight >= threshold override fun getAlgorithm() = ALGORITHM
} override fun getEncoded(): ByteArray = this.serialize().bytes
override fun getFormat() = FORMAT
override val keys: Set<PublicKey> get() = children.flatMap { it.keys }.toSet() /**
* Function checks if the public keys corresponding to the signatures are matched against the leaves of the composite
override fun toString() = "(${children.joinToString()})" * key tree in question, and the total combined weight of all children is calculated for every intermediary node.
* If all thresholds are satisfied, the composite key requirement is considered to be met.
*/
fun isFulfilledBy(keysToCheck: Iterable<PublicKey>): Boolean {
if (keysToCheck.any { it is CompositeKey } ) return false
val totalWeight = children.map { (node, weight) ->
if (node is CompositeKey) {
if (node.isFulfilledBy(keysToCheck)) weight else 0
} else {
if (keysToCheck.contains(node)) weight else 0
}
}.sum()
return totalWeight >= threshold
} }
/** A helper class for building a [CompositeKey.Node]. */ /**
* Set of all leaf keys of that CompositeKey.
*/
val leavesKeys: Set<PublicKey>
get() = children.flatMap { it.node.keys }.toSet() // Uses PublicKey.keys extension.
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is CompositeKey) return false
if (threshold != other.threshold) return false
if (children != other.children) return false
return true
}
override fun hashCode(): Int {
var result = threshold
result = 31 * result + children.hashCode()
return result
}
override fun toString() = "(${children.joinToString()})"
/** A helper class for building a [CompositeKey]. */
class Builder { class Builder {
private val children: MutableList<CompositeKey> = mutableListOf() private val children: MutableList<NodeAndWeight> = mutableListOf()
private val weights: MutableList<Int> = mutableListOf()
/** Adds a child [CompositeKey] node. Specifying a [weight] for the child is optional and will default to 1. */ /** Adds a child [CompositeKey] node. Specifying a [weight] for the child is optional and will default to 1. */
fun addKey(key: CompositeKey, weight: Int = 1): Builder { fun addKey(key: PublicKey, weight: Int = 1): Builder {
children.add(key) children.add(NodeAndWeight(key, weight))
weights.add(weight)
return this return this
} }
fun addKeys(vararg keys: CompositeKey): Builder { fun addKeys(vararg keys: PublicKey): Builder {
keys.forEach { addKey(it) } keys.forEach { addKey(it) }
return this return this
} }
fun addKeys(keys: List<CompositeKey>): Builder = addKeys(*keys.toTypedArray()) fun addKeys(keys: List<PublicKey>): Builder = addKeys(*keys.toTypedArray())
/** /**
* Builds the [CompositeKey.Node]. If [threshold] is not specified, it will default to * Builds the [CompositeKey]. If [threshold] is not specified, it will default to
* the size of the children, effectively generating an "N of N" requirement. * the size of the children, effectively generating an "N of N" requirement.
* During process removes single keys wrapped in [CompositeKey] and enforces ordering on child nodes.
*/ */
fun build(threshold: Int? = null): CompositeKey.Node { @Throws(IllegalArgumentException::class)
return Node(threshold ?: children.size, children.toList(), weights.toList()) fun build(threshold: Int? = null): PublicKey {
val n = children.size
if (n > 1)
return CompositeKey(threshold ?: n, children)
else if (n == 1) {
require(threshold == null || threshold == children.first().weight)
{ "Trying to build invalid CompositeKey, threshold value different than weight of single child node." }
return children.first().node // We can assume that this node is a correct CompositeKey.
}
else throw IllegalArgumentException("Trying to build CompositeKey without child nodes.")
} }
} }
/**
* Returns the enclosed [PublicKey] for a [CompositeKey] with a single leaf node
*
* @throws IllegalArgumentException if the [CompositeKey] contains more than one node
*/
val singleKey: PublicKey
get() = keys.singleOrNull() ?: throw IllegalStateException("The key is composed of more than one PublicKey primitive")
} }
/** Returns the set of all [PublicKey]s contained in the leaves of the [CompositeKey]s */ /**
val Iterable<CompositeKey>.keys: Set<PublicKey> * Expands all [CompositeKey]s present in PublicKey iterable to set of single [PublicKey]s.
* If an element of the set is a single PublicKey it gives just that key, if it is a [CompositeKey] it returns all leaf
* keys for that composite element.
*/
val Iterable<PublicKey>.expandedCompositeKeys: Set<PublicKey>
get() = flatMap { it.keys }.toSet() get() = flatMap { it.keys }.toSet()

View File

@ -4,6 +4,8 @@ package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
@ -12,6 +14,7 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import java.math.BigInteger import java.math.BigInteger
import java.security.InvalidKeyException
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
@ -27,7 +30,7 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
} }
// TODO: consider removing this as whoever needs to identify the signer should be able to derive it from the public key // TODO: consider removing this as whoever needs to identify the signer should be able to derive it from the public key
class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey.singleKey, bits) class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey, bits)
} }
@CordaSerializable @CordaSerializable
@ -39,7 +42,6 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun toString() = "NULL_KEY" override fun toString() = "NULL_KEY"
} }
val NullCompositeKey = NullPublicKey.composite
// TODO: Clean up this duplication between Null and Dummy public key // TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable @CordaSerializable
@ -72,22 +74,37 @@ fun PrivateKey.signWithECDSA(bytesToSign: ByteArray, publicKey: PublicKey): Digi
val ed25519Curve = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512) val ed25519Curve = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512)
fun parsePublicKeyBase58(base58String: String) = EdDSAPublicKey(EdDSAPublicKeySpec(Base58.decode(base58String), ed25519Curve)) // TODO We use for both CompositeKeys and EdDSAPublicKey custom Kryo serializers and deserializers. We need to specify encoding.
fun PublicKey.toBase58String() = Base58.encode((this as EdDSAPublicKey).abyte) // TODO: follow the crypto-conditions ASN.1 spec, some changes are needed to be compatible with the condition
// structure, e.g. mapping a PublicKey to a condition with the specific feature (ED25519).
fun parsePublicKeyBase58(base58String: String): PublicKey = Base58.decode(base58String).deserialize<PublicKey>()
fun PublicKey.toBase58String(): String = Base58.encode(this.serialize().bytes)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray) = private.signWithECDSA(bytesToSign, public) fun KeyPair.signWithECDSA(bytesToSign: ByteArray) = private.signWithECDSA(bytesToSign, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes) = private.signWithECDSA(bytesToSign.bytes, public) fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes) = private.signWithECDSA(bytesToSign.bytes, public)
fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes, party: Party) = signWithECDSA(bytesToSign.bytes, party) fun KeyPair.signWithECDSA(bytesToSign: OpaqueBytes, party: Party) = signWithECDSA(bytesToSign.bytes, party)
// TODO This case will need more careful thinking, as party owningKey can be a CompositeKey. One way of doing that is
// implementation of CompositeSignature.
@Throws(InvalidKeyException::class)
fun KeyPair.signWithECDSA(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable { fun KeyPair.signWithECDSA(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
check(public in party.owningKey.keys)
val sig = signWithECDSA(bytesToSign) val sig = signWithECDSA(bytesToSign)
val sigKey = when (party.owningKey) { // Quick workaround when we have CompositeKey as Party owningKey.
is CompositeKey -> throw InvalidKeyException("Signing for parties with CompositeKey not supported.")
else -> party.owningKey
}
sigKey.verifyWithECDSA(bytesToSign, sig)
return DigitalSignature.LegallyIdentifiable(party, sig.bytes) return DigitalSignature.LegallyIdentifiable(party, sig.bytes)
} }
/** Utility to simplify the act of verifying a signature */ /** Utility to simplify the act of verifying a signature */
@Throws(SignatureException::class, IllegalStateException::class)
fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) { fun PublicKey.verifyWithECDSA(content: ByteArray, signature: DigitalSignature) {
val pubKey = when (this) {
is CompositeKey -> throw IllegalStateException("Verification of CompositeKey signatures currently not supported.") // TODO CompositeSignature verification.
else -> this
}
val verifier = EdDSAEngine() val verifier = EdDSAEngine()
verifier.initVerify(this) verifier.initVerify(pubKey)
verifier.update(content) verifier.update(content)
if (verifier.verify(signature.bytes) == false) if (verifier.verify(signature.bytes) == false)
throw SignatureException("Signature did not match") throw SignatureException("Signature did not match")
@ -100,8 +117,22 @@ fun PublicKey.toStringShort(): String {
} ?: toString() } ?: toString()
} }
/** Creates a [CompositeKey] with a single leaf node containing the public key */ val PublicKey.keys: Set<PublicKey> get() {
val PublicKey.composite: CompositeKey get() = CompositeKey.Leaf(this) return if (this is CompositeKey) this.leavesKeys
else setOf(this)
}
fun PublicKey.isFulfilledBy(otherKey: PublicKey): Boolean = isFulfilledBy(setOf(otherKey))
fun PublicKey.isFulfilledBy(otherKeys: Iterable<PublicKey>): Boolean {
return if (this is CompositeKey) this.isFulfilledBy(otherKeys)
else this in otherKeys
}
/** Checks whether any of the given [keys] matches a leaf on the CompositeKey tree or a single PublicKey */
fun PublicKey.containsAny(otherKeys: Iterable<PublicKey>): Boolean {
return if (this is CompositeKey) keys.intersect(otherKeys).isNotEmpty()
else this in otherKeys
}
/** Returns the set of all [PublicKey]s of the signatures */ /** Returns the set of all [PublicKey]s of the signatures */
fun Iterable<DigitalSignature.WithKey>.byKeys() = map { it.by }.toSet() fun Iterable<DigitalSignature.WithKey>.byKeys() = map { it.by }.toSet()

View File

@ -7,7 +7,7 @@ import java.security.PublicKey
/** /**
* The [Party] class represents an entity on the network, which is typically identified by a legal [name] and public key * The [Party] class represents an entity on the network, which is typically identified by a legal [name] and public key
* that it can sign transactions under. As parties may use multiple keys for signing and, for example, have offline backup * that it can sign transactions under. As parties may use multiple keys for signing and, for example, have offline backup
* keys, the "public key" of a party is represented by a composite construct a [CompositeKey], which combines multiple * keys, the "public key" of a party can be represented by a composite construct a [CompositeKey], which combines multiple
* cryptographic public key primitives into a tree structure. * cryptographic public key primitives into a tree structure.
* *
* For example: Alice has two key pairs (pub1/priv1 and pub2/priv2), and wants to be able to sign transactions with either of them. * For example: Alice has two key pairs (pub1/priv1 and pub2/priv2), and wants to be able to sign transactions with either of them.
@ -22,10 +22,7 @@ import java.security.PublicKey
* *
* @see CompositeKey * @see CompositeKey
*/ */
class Party(val name: String, owningKey: CompositeKey) : AbstractParty(owningKey) { class Party(val name: String, owningKey: PublicKey) : AbstractParty(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 toAnonymous(): AnonymousParty = AnonymousParty(owningKey) override fun toAnonymous(): AnonymousParty = AnonymousParty(owningKey)
override fun toString() = "${owningKey.toBase58String()} (${name})" override fun toString() = "${owningKey.toBase58String()} (${name})"
override fun nameOrNull(): String? = name override fun nameOrNull(): String? = name

View File

@ -5,7 +5,6 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UpgradedContract import net.corda.core.contracts.UpgradedContract
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
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
@ -18,6 +17,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import rx.Observable import rx.Observable
import java.io.InputStream import java.io.InputStream
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -158,7 +158,7 @@ interface CordaRPCOps : RPCOps {
/** /**
* Returns the [Party] corresponding to the given key, if found. * Returns the [Party] corresponding to the given key, if found.
*/ */
fun partyFromKey(key: CompositeKey): Party? fun partyFromKey(key: PublicKey): Party?
/** /**
* Returns the [Party] with the given name as it's [Party.name] * Returns the [Party] with the given name as it's [Party.name]

View File

@ -1,6 +1,7 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.keys
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowStateMachine import net.corda.core.flows.FlowStateMachine
import net.corda.core.messaging.MessagingService import net.corda.core.messaging.MessagingService
@ -109,6 +110,8 @@ interface ServiceHub : ServicesForResolution {
* used in contexts where the Node knows it is hosting a Notary Service. Otherwise, it will throw * used in contexts where the Node knows it is hosting a Notary Service. Otherwise, it will throw
* an IllegalArgumentException. * an IllegalArgumentException.
* Typical use is during signing in flows and for unit test signing. * Typical use is during signing in flows and for unit test signing.
*
* TODO: same problem as with legalIdentityKey.
*/ */
val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey.keys) val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey.keys)
} }

View File

@ -2,8 +2,8 @@ package net.corda.core.node.services
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.AnonymousParty 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.security.PublicKey
/** /**
* An identity service maintains an bidirectional map of [Party]s to their associated public keys and thus supports * An identity service maintains an bidirectional map of [Party]s to their associated public keys and thus supports
@ -23,7 +23,7 @@ interface IdentityService {
// indefinitely. It may be that in the long term we need to drop or archive very old Party information for space, // indefinitely. It may be that in the long term we need to drop or archive very old Party information for space,
// but for now this is not supported. // but for now this is not supported.
fun partyFromKey(key: CompositeKey): Party? fun partyFromKey(key: PublicKey): Party?
fun partyFromName(name: String): Party? fun partyFromName(name: String): Party?
fun partyFromAnonymous(party: AnonymousParty): Party? fun partyFromAnonymous(party: AnonymousParty): Party?

View File

@ -3,7 +3,6 @@ package net.corda.core.node.services
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.messaging.MessagingService import net.corda.core.messaging.MessagingService
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
@ -11,6 +10,7 @@ import net.corda.core.node.NodeInfo
import net.corda.core.randomOrNull import net.corda.core.randomOrNull
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import rx.Observable import rx.Observable
import java.security.PublicKey
/** /**
* A network map contains lists of nodes on the network along with information about their identity keys, services * A network map contains lists of nodes on the network along with information about their identity keys, services
@ -74,11 +74,11 @@ interface NetworkMapCache {
*/ */
/** Look up the node info for a specific peer key. */ /** Look up the node info for a specific peer key. */
fun getNodeByLegalIdentityKey(compositeKey: CompositeKey): NodeInfo? fun getNodeByLegalIdentityKey(identityKey: PublicKey): NodeInfo?
/** Look up all nodes advertising the service owned by [compositeKey] */ /** Look up all nodes advertising the service owned by [publicKey] */
fun getNodesByAdvertisedServiceIdentityKey(compositeKey: CompositeKey): List<NodeInfo> { fun getNodesByAdvertisedServiceIdentityKey(publicKey: PublicKey): List<NodeInfo> {
return partyNodes.filter { it.advertisedServices.any { it.identity.owningKey == compositeKey } } return partyNodes.filter { it.advertisedServices.any { it.identity.owningKey == publicKey } }
} }
/** Returns information about the party, which may be a specific node or a service */ /** Returns information about the party, which may be a specific node or a service */

View File

@ -204,8 +204,8 @@ interface VaultService {
@Suspendable @Suspendable
fun generateSpend(tx: TransactionBuilder, fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: CompositeKey, to: PublicKey,
onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<CompositeKey>> onlyFromParties: Set<AbstractParty>? = null): Pair<TransactionBuilder, List<PublicKey>>
// DOCSTART VaultStatesQuery // DOCSTART VaultStatesQuery
/** /**
@ -288,11 +288,19 @@ interface KeyManagementService {
/** Returns a snapshot of the current pubkey->privkey mapping. */ /** Returns a snapshot of the current pubkey->privkey mapping. */
val keys: Map<PublicKey, PrivateKey> val keys: Map<PublicKey, PrivateKey>
@Throws(IllegalStateException::class)
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key ${publicKey.toStringShort()}") fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key ${publicKey.toStringShort()}")
fun toKeyPair(publicKey: PublicKey) = KeyPair(publicKey, toPrivate(publicKey)) @Throws(IllegalArgumentException::class)
fun toKeyPair(publicKey: PublicKey): KeyPair {
when (publicKey) {
is CompositeKey -> throw IllegalArgumentException("Got CompositeKey when single PublicKey expected.")
else -> return KeyPair(publicKey, toPrivate(publicKey))
}
}
/** Returns the first [KeyPair] matching any of the [publicKeys] */ /** Returns the first [KeyPair] matching any of the [publicKeys] */
@Throws(IllegalArgumentException::class)
fun toKeyPair(publicKeys: Iterable<PublicKey>) = publicKeys.first { keys.contains(it) }.let { toKeyPair(it) } fun toKeyPair(publicKeys: Iterable<PublicKey>) = publicKeys.first { keys.contains(it) }.let { toKeyPair(it) }
/** Generates a new random key and adds it to the exposed map. */ /** Generates a new random key and adds it to the exposed map. */

View File

@ -65,8 +65,7 @@ object DefaultKryoCustomizer {
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer) register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
// Using a custom serializer for compactness // Using a custom serializer for compactness
register(CompositeKey.Node::class.java, CompositeKeyNodeSerializer) register(CompositeKey::class.java, CompositeKeySerializer)
register(CompositeKey.Leaf::class.java, CompositeKeyLeafSerializer)
// Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway. // Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway.
register(Array<StackTraceElement>::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> }) register(Array<StackTraceElement>::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> })

View File

@ -330,7 +330,7 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
val outputs = kryo.readClassAndObject(input) as List<TransactionState<ContractState>> val outputs = kryo.readClassAndObject(input) as List<TransactionState<ContractState>>
val commands = kryo.readClassAndObject(input) as List<Command> val commands = kryo.readClassAndObject(input) as List<Command>
val notary = kryo.readClassAndObject(input) as Party? val notary = kryo.readClassAndObject(input) as Party?
val signers = kryo.readClassAndObject(input) as List<CompositeKey> val signers = kryo.readClassAndObject(input) as List<PublicKey>
val transactionType = kryo.readClassAndObject(input) as TransactionType val transactionType = kryo.readClassAndObject(input) as TransactionType
val timestamp = kryo.readClassAndObject(input) as Timestamp? val timestamp = kryo.readClassAndObject(input) as Timestamp?
@ -367,41 +367,38 @@ object Ed25519PublicKeySerializer : Serializer<EdDSAPublicKey>() {
} }
} }
/** For serialising composite keys */ // TODO Implement standardized serialization of CompositeKeys. See JIRA issue: CORDA-249.
@ThreadSafe @ThreadSafe
object CompositeKeyLeafSerializer : Serializer<CompositeKey.Leaf>() { object CompositeKeySerializer : Serializer<CompositeKey>() {
override fun write(kryo: Kryo, output: Output, obj: CompositeKey.Leaf) { override fun write(kryo: Kryo, output: Output, obj: CompositeKey) {
val key = obj.publicKey
kryo.writeClassAndObject(output, key)
}
override fun read(kryo: Kryo, input: Input, type: Class<CompositeKey.Leaf>): CompositeKey.Leaf {
val key = kryo.readClassAndObject(input) as PublicKey
return CompositeKey.Leaf(key)
}
}
@ThreadSafe
object CompositeKeyNodeSerializer : Serializer<CompositeKey.Node>() {
override fun write(kryo: Kryo, output: Output, obj: CompositeKey.Node) {
output.writeInt(obj.threshold) output.writeInt(obj.threshold)
output.writeInt(obj.children.size) output.writeInt(obj.children.size)
obj.children.forEach { kryo.writeClassAndObject(output, it) } obj.children.forEach { kryo.writeClassAndObject(output, it) }
output.writeInts(obj.weights.toIntArray())
} }
override fun read(kryo: Kryo, input: Input, type: Class<CompositeKey.Node>): CompositeKey.Node { override fun read(kryo: Kryo, input: Input, type: Class<CompositeKey>): CompositeKey {
val threshold = input.readInt() val threshold = input.readInt()
val childCount = input.readInt() val children = readListOfLength<CompositeKey.NodeAndWeight>(kryo, input, minLen = 2)
val children = (1..childCount).map { kryo.readClassAndObject(input) as CompositeKey }
val weights = input.readInts(childCount)
val builder = CompositeKey.Builder() val builder = CompositeKey.Builder()
weights.zip(children).forEach { builder.addKey(it.second, it.first) } children.forEach { builder.addKey(it.node, it.weight) }
return builder.build(threshold) return builder.build(threshold) as CompositeKey
} }
} }
/**
* Helper function for reading lists with number of elements at the beginning.
* @param minLen minimum number of elements we expect for list to include, defaults to 1
* @param expectedLen expected length of the list, defaults to null if arbitrary length list read
*/
inline fun <reified T> readListOfLength(kryo: Kryo, input: Input, minLen: Int = 1, expectedLen: Int? = null): List<T> {
val elemCount = input.readInt()
if (elemCount < minLen) throw KryoException("Cannot deserialize list, too little elements. Minimum required: $minLen, got: $elemCount")
if (expectedLen != null && elemCount != expectedLen)
throw KryoException("Cannot deserialize list, expected length: $expectedLen, got: $elemCount.")
val list = (1..elemCount).map { kryo.readClassAndObject(input) as T }
return list
}
/** Marker interface for kotlin object definitions so that they are deserialized as the singleton instance. */ /** Marker interface for kotlin object definitions so that they are deserialized as the singleton instance. */
interface DeserializeAsKotlinObjectDef interface DeserializeAsKotlinObjectDef

View File

@ -1,8 +1,8 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.security.PublicKey
import java.util.* import java.util.*
/** /**
@ -20,14 +20,14 @@ abstract class BaseTransaction(
*/ */
val notary: Party?, val notary: Party?,
/** /**
* Composite keys that need to be fulfilled by signatures in order for the transaction to be valid. * Public keys that need to be fulfilled by signatures in order for the transaction to be valid.
* In a [SignedTransaction] this list is used to check whether there are any missing signatures. Note that * In a [SignedTransaction] this list is used to check whether there are any missing signatures. Note that
* there is nothing that forces the list to be the _correct_ list of signers for this transaction until * there is nothing that forces the list to be the _correct_ list of signers for this transaction until
* the transaction is verified by using [LedgerTransaction.verify]. * the transaction is verified by using [LedgerTransaction.verify].
* *
* It includes the notary key, if the notary field is set. * It includes the notary key, if the notary field is set.
*/ */
val mustSign: List<CompositeKey>, val mustSign: List<PublicKey>,
/** /**
* Pointer to a class that defines the behaviour of this transaction: either normal, or "notary changing". * Pointer to a class that defines the behaviour of this transaction: either normal, or "notary changing".
*/ */

View File

@ -1,10 +1,10 @@
package net.corda.core.transactions package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
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
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import java.security.PublicKey
/** /**
* A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations: * A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations:
@ -29,7 +29,7 @@ class LedgerTransaction(
/** The hash of the original serialised WireTransaction. */ /** The hash of the original serialised WireTransaction. */
override val id: SecureHash, override val id: SecureHash,
notary: Party?, notary: Party?,
signers: List<CompositeKey>, signers: List<PublicKey>,
timestamp: Timestamp?, timestamp: Timestamp?,
type: TransactionType type: TransactionType
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) { ) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {

View File

@ -5,6 +5,7 @@ import net.corda.core.crypto.*
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.p2PKryo import net.corda.core.serialization.p2PKryo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import java.security.PublicKey
import net.corda.core.serialization.withoutReferences import net.corda.core.serialization.withoutReferences
fun <T : Any> serializedHash(x: T): SecureHash { fun <T : Any> serializedHash(x: T): SecureHash {
@ -26,7 +27,7 @@ interface TraversableTransaction {
val outputs: List<TransactionState<ContractState>> val outputs: List<TransactionState<ContractState>>
val commands: List<Command> val commands: List<Command>
val notary: Party? val notary: Party?
val mustSign: List<CompositeKey> val mustSign: List<PublicKey>
val type: TransactionType? val type: TransactionType?
val timestamp: Timestamp? val timestamp: Timestamp?
@ -74,7 +75,7 @@ class FilteredLeaves(
override val outputs: List<TransactionState<ContractState>>, override val outputs: List<TransactionState<ContractState>>,
override val commands: List<Command>, override val commands: List<Command>,
override val notary: Party?, override val notary: Party?,
override val mustSign: List<CompositeKey>, override val mustSign: List<PublicKey>,
override val type: TransactionType?, override val type: TransactionType?,
override val timestamp: Timestamp? override val timestamp: Timestamp?
) : TraversableTransaction { ) : TraversableTransaction {

View File

@ -3,21 +3,22 @@ package net.corda.core.transactions
import net.corda.core.contracts.AttachmentResolutionException import net.corda.core.contracts.AttachmentResolutionException
import net.corda.core.contracts.NamedByHash import net.corda.core.contracts.NamedByHash
import net.corda.core.contracts.TransactionResolutionException import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.crypto.CompositeKey import net.corda.core.node.ServiceHub
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.signWithECDSA import net.corda.core.crypto.signWithECDSA
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.* import java.util.*
/** /**
* SignedTransaction wraps a serialized WireTransaction. It contains one or more signatures, each one for * SignedTransaction wraps a serialized WireTransaction. It contains one or more signatures, each one for
* a public key that is mentioned inside a transaction command. SignedTransaction is the top level transaction type * a public key (including composite keys) that is mentioned inside a transaction command. SignedTransaction is the top level transaction type
* and the type most frequently passed around the network and stored. The identity of a transaction is the hash * and the type most frequently passed around the network and stored. The identity of a transaction is the hash of Merkle root
* of a WireTransaction, therefore if you are storing data keyed by WT hash be aware that multiple different STs may * of a WireTransaction, therefore if you are storing data keyed by WT hash be aware that multiple different STs may
* map to the same key (and they could be different in important ways, like validity!). The signatures on a * map to the same key (and they could be different in important ways, like validity!). The signatures on a
* SignedTransaction might be invalid or missing: the type does not imply validity. * SignedTransaction might be invalid or missing: the type does not imply validity.
@ -43,7 +44,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
override val id: SecureHash get() = tx.id override val id: SecureHash get() = tx.id
@CordaSerializable @CordaSerializable
class SignaturesMissingException(val missing: Set<CompositeKey>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() { class SignaturesMissingException(val missing: Set<PublicKey>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() {
override fun toString(): String { override fun toString(): String {
return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}" return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}"
} }
@ -62,13 +63,13 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
* @throws SignaturesMissingException if any signatures should have been present but were not. * @throws SignaturesMissingException if any signatures should have been present but were not.
*/ */
@Throws(SignatureException::class) @Throws(SignatureException::class)
fun verifySignatures(vararg allowedToBeMissing: CompositeKey): WireTransaction { fun verifySignatures(vararg allowedToBeMissing: PublicKey): WireTransaction {
// Embedded WireTransaction is not deserialised until after we check the signatures. // Embedded WireTransaction is not deserialised until after we check the signatures.
checkSignaturesAreValid() checkSignaturesAreValid()
val missing = getMissingSignatures() val missing = getMissingSignatures()
if (missing.isNotEmpty()) { if (missing.isNotEmpty()) {
val allowed = setOf(*allowedToBeMissing) val allowed = allowedToBeMissing.toSet()
val needed = missing - allowed val needed = missing - allowed
if (needed.isNotEmpty()) if (needed.isNotEmpty())
throw SignaturesMissingException(needed, getMissingKeyDescriptions(needed), id) throw SignaturesMissingException(needed, getMissingKeyDescriptions(needed), id)
@ -92,8 +93,10 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
} }
} }
private fun getMissingSignatures(): Set<CompositeKey> { private fun getMissingSignatures(): Set<PublicKey> {
val sigKeys = sigs.map { it.by }.toSet() val sigKeys = sigs.map { it.by }.toSet()
// TODO Problem is that we can get single PublicKey wrapped as CompositeKey in allowedToBeMissing/mustSign
// equals on CompositeKey won't catch this case (do we want to single PublicKey be equal to the same key wrapped in CompositeKey with threshold 1?)
val missing = tx.mustSign.filter { !it.isFulfilledBy(sigKeys) }.toSet() val missing = tx.mustSign.filter { !it.isFulfilledBy(sigKeys) }.toSet()
return missing return missing
} }
@ -102,7 +105,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
* Get a human readable description of where signatures are required from, and are missing, to assist in debugging * Get a human readable description of where signatures are required from, and are missing, to assist in debugging
* the underlying cause. * the underlying cause.
*/ */
private fun getMissingKeyDescriptions(missing: Set<CompositeKey>): ArrayList<String> { private fun getMissingKeyDescriptions(missing: Set<PublicKey>): ArrayList<String> {
// TODO: We need a much better way of structuring this data // TODO: We need a much better way of structuring this data
val missingElements = ArrayList<String>() val missingElements = ArrayList<String>()
this.tx.commands.forEach { command -> this.tx.commands.forEach { command ->

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.*
import net.corda.core.flows.FlowStateMachine import net.corda.core.flows.FlowStateMachine
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -34,7 +35,7 @@ open class TransactionBuilder(
protected val attachments: MutableList<SecureHash> = arrayListOf(), protected val attachments: MutableList<SecureHash> = arrayListOf(),
protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(), protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(),
protected val commands: MutableList<Command> = arrayListOf(), protected val commands: MutableList<Command> = arrayListOf(),
protected val signers: MutableSet<CompositeKey> = mutableSetOf(), protected val signers: MutableSet<PublicKey> = mutableSetOf(),
protected var timestamp: Timestamp? = null) { protected var timestamp: Timestamp? = null) {
val time: Timestamp? get() = timestamp val time: Timestamp? get() = timestamp
@ -135,7 +136,7 @@ open class TransactionBuilder(
fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction { fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction {
if (checkSufficientSignatures) { if (checkSufficientSignatures) {
val gotKeys = currentSigs.map { it.by }.toSet() val gotKeys = currentSigs.map { it.by }.toSet()
val missing: Set<CompositeKey> = signers.filter { !it.isFulfilledBy(gotKeys) }.toSet() val missing: Set<PublicKey> = signers.filter { !it.isFulfilledBy(gotKeys) }.toSet()
if (missing.isNotEmpty()) if (missing.isNotEmpty())
throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.joinToString()}") throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.joinToString()}")
} }
@ -178,8 +179,8 @@ open class TransactionBuilder(
commands.add(arg) commands.add(arg)
} }
fun addCommand(data: CommandData, vararg keys: CompositeKey) = addCommand(Command(data, listOf(*keys))) fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys)))
fun addCommand(data: CommandData, keys: List<CompositeKey>) = addCommand(Command(data, keys)) fun addCommand(data: CommandData, keys: List<PublicKey>) = addCommand(Command(data, keys))
// Accessors that yield immutable snapshots. // Accessors that yield immutable snapshots.
fun inputStates(): List<StateRef> = ArrayList(inputs) fun inputStates(): List<StateRef> = ArrayList(inputs)

View File

@ -2,7 +2,6 @@ package net.corda.core.transactions
import com.esotericsoftware.kryo.pool.KryoPool import com.esotericsoftware.kryo.pool.KryoPool
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.MerkleTree import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -30,7 +29,7 @@ class WireTransaction(
/** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */ /** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */
override val commands: List<Command>, override val commands: List<Command>,
notary: Party?, notary: Party?,
signers: List<CompositeKey>, signers: List<PublicKey>,
type: TransactionType, type: TransactionType,
timestamp: Timestamp? timestamp: Timestamp?
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp), TraversableTransaction { ) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp), TraversableTransaction {
@ -87,7 +86,7 @@ class WireTransaction(
*/ */
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class) @Throws(AttachmentResolutionException::class, TransactionResolutionException::class)
fun toLedgerTransaction( fun toLedgerTransaction(
resolveIdentity: (CompositeKey) -> Party?, resolveIdentity: (PublicKey) -> Party?,
resolveAttachment: (SecureHash) -> Attachment?, resolveAttachment: (SecureHash) -> Attachment?,
resolveStateRef: (StateRef) -> TransactionState<*>? resolveStateRef: (StateRef) -> TransactionState<*>?
): LedgerTransaction { ): LedgerTransaction {

View File

@ -1,8 +1,8 @@
package net.corda.core.utilities package net.corda.core.utilities
import net.corda.core.ErrorOr import net.corda.core.ErrorOr
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.parsePublicKeyBase58
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
@ -18,7 +18,7 @@ class ApiUtils(val rpc: CordaRPCOps) {
*/ */
fun withParty(partyKeyStr: String, notFound: (String) -> Response = defaultNotFound, found: (Party) -> Response): Response { fun withParty(partyKeyStr: String, notFound: (String) -> Response = defaultNotFound, found: (Party) -> Response): Response {
val party = try { val party = try {
val partyKey = CompositeKey.parseFromBase58(partyKeyStr) val partyKey = parsePublicKeyBase58(partyKeyStr)
ErrorOr(rpc.partyFromKey(partyKey)) ErrorOr(rpc.partyFromKey(partyKey))
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
ErrorOr.of(Exception("Invalid base58 key passed for party key $e")) ErrorOr.of(Exception("Invalid base58 key passed for party key $e"))

View File

@ -5,13 +5,14 @@ package net.corda.core.utilities
import net.corda.core.crypto.* import net.corda.core.crypto.*
import java.math.BigInteger import java.math.BigInteger
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.time.Instant import java.time.Instant
// A dummy time at which we will be pretending test transactions are created. // A dummy time at which we will be pretending test transactions are created.
val TEST_TX_TIME: Instant get() = Instant.parse("2015-04-17T12:00:00.00Z") val TEST_TX_TIME: Instant get() = Instant.parse("2015-04-17T12:00:00.00Z")
val DUMMY_PUBKEY_1: CompositeKey get() = DummyPublicKey("x1").composite val DUMMY_PUBKEY_1: PublicKey get() = DummyPublicKey("x1")
val DUMMY_PUBKEY_2: CompositeKey get() = DummyPublicKey("x2").composite val DUMMY_PUBKEY_2: PublicKey get() = DummyPublicKey("x2")
val DUMMY_KEY_1: KeyPair by lazy { generateKeyPair() } val DUMMY_KEY_1: KeyPair by lazy { generateKeyPair() }
val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() } val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() }

View File

@ -4,10 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
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.CompositeKey import net.corda.core.crypto.*
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.signWithECDSA
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
@ -16,6 +13,7 @@ import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import java.security.PublicKey
/** /**
* Abstract flow to be used for replacing one state with another, for example when changing the notary of a state. * Abstract flow to be used for replacing one state with another, for example when changing the notary of a state.
@ -74,10 +72,10 @@ abstract class AbstractStateReplacementFlow {
return finalTx.tx.outRef(0) return finalTx.tx.outRef(0)
} }
abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>> abstract protected fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>>
@Suspendable @Suspendable
private fun collectSignatures(participants: Iterable<CompositeKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> { private fun collectSignatures(participants: Iterable<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
val parties = participants.map { val parties = participants.map {
val participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?: val participantNode = serviceHub.networkMapCache.getNodeByLegalIdentityKey(it) ?:
throw IllegalStateException("Participant $it to state $originalState not found on the network") throw IllegalStateException("Participant $it to state $originalState not found on the network")

View File

@ -2,13 +2,13 @@ package net.corda.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.flows.AbstractStateReplacementFlow.Proposal import net.corda.flows.AbstractStateReplacementFlow.Proposal
import net.corda.flows.ContractUpgradeFlow.Acceptor import net.corda.flows.ContractUpgradeFlow.Acceptor
import net.corda.flows.ContractUpgradeFlow.Instigator import net.corda.flows.ContractUpgradeFlow.Instigator
import java.security.PublicKey
/** /**
* A flow to be used for upgrading state objects of an old contract to a new contract. * A flow to be used for upgrading state objects of an old contract to a new contract.
@ -28,8 +28,8 @@ object ContractUpgradeFlow {
@JvmStatic @JvmStatic
fun verify(input: ContractState, output: ContractState, commandData: Command) { fun verify(input: ContractState, output: ContractState, commandData: Command) {
val command = commandData.value as UpgradeCommand val command = commandData.value as UpgradeCommand
val participants: Set<CompositeKey> = input.participants.toSet() val participants: Set<PublicKey> = input.participants.toSet()
val keysThatSigned: Set<CompositeKey> = commandData.signers.toSet() val keysThatSigned: Set<PublicKey> = commandData.signers.toSet()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *> val upgradedContract = command.upgradedContractClass.newInstance() as UpgradedContract<ContractState, *>
requireThat { requireThat {
@ -54,7 +54,7 @@ object ContractUpgradeFlow {
newContractClass: Class<out UpgradedContract<OldState, NewState>> newContractClass: Class<out UpgradedContract<OldState, NewState>>
) : AbstractStateReplacementFlow.Instigator<OldState, NewState, Class<out UpgradedContract<OldState, NewState>>>(originalState, newContractClass) { ) : AbstractStateReplacementFlow.Instigator<OldState, NewState, Class<out UpgradedContract<OldState, NewState>>>(originalState, newContractClass) {
override fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>> { override fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>> {
val stx = assembleBareTx(originalState, modification) val stx = assembleBareTx(originalState, modification)
.signWith(serviceHub.legalIdentityKey) .signWith(serviceHub.legalIdentityKey)
.toSignedTransaction(false) .toSignedTransaction(false)

View File

@ -5,6 +5,7 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction

View File

@ -1,13 +1,13 @@
package net.corda.flows package net.corda.flows
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.flows.NotaryChangeFlow.Acceptor import net.corda.flows.NotaryChangeFlow.Acceptor
import net.corda.flows.NotaryChangeFlow.Instigator import net.corda.flows.NotaryChangeFlow.Instigator
import java.security.PublicKey
/** /**
* A flow to be used for changing a state's Notary. This is required since all input states to a transaction * A flow to be used for changing a state's Notary. This is required since all input states to a transaction
@ -25,11 +25,11 @@ object NotaryChangeFlow : AbstractStateReplacementFlow() {
newNotary: Party, newNotary: Party,
progressTracker: ProgressTracker = tracker()) : AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) { progressTracker: ProgressTracker = tracker()) : AbstractStateReplacementFlow.Instigator<T, T, Party>(originalState, newNotary, progressTracker) {
override fun assembleTx(): Pair<SignedTransaction, Iterable<CompositeKey>> { override fun assembleTx(): Pair<SignedTransaction, Iterable<PublicKey>> {
val state = originalState.state val state = originalState.state
val tx = TransactionType.NotaryChange.Builder(originalState.state.notary) val tx = TransactionType.NotaryChange.Builder(originalState.state.notary)
val participants: Iterable<CompositeKey> val participants: Iterable<PublicKey>
if (state.encumbrance == null) { if (state.encumbrance == null) {
val modifiedState = TransactionState(state.data, modification) val modifiedState = TransactionState(state.data, modification)
@ -54,14 +54,14 @@ object NotaryChangeFlow : AbstractStateReplacementFlow() {
* *
* @return union of all added states' participants * @return union of all added states' participants
*/ */
private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<CompositeKey> { private fun resolveEncumbrances(tx: TransactionBuilder): Iterable<PublicKey> {
val stateRef = originalState.ref val stateRef = originalState.ref
val txId = stateRef.txhash val txId = stateRef.txhash
val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId) val issuingTx = serviceHub.storageService.validatedTransactions.getTransaction(txId)
?: throw StateReplacementException("Transaction $txId not found") ?: throw StateReplacementException("Transaction $txId not found")
val outputs = issuingTx.tx.outputs val outputs = issuingTx.tx.outputs
val participants = mutableSetOf<CompositeKey>() val participants = mutableSetOf<PublicKey>()
var nextStateIndex = stateRef.index var nextStateIndex = stateRef.index
var newOutputPosition = tx.outputStates().size var newOutputPosition = tx.outputStates().size

View File

@ -18,6 +18,7 @@ import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
/** /**
* Classes for manipulating a two party deal or agreement. * Classes for manipulating a two party deal or agreement.
@ -43,7 +44,7 @@ object TwoPartyDealFlow {
// This object is serialised to the network and is the first flow message the seller sends to the buyer. // This object is serialised to the network and is the first flow message the seller sends to the buyer.
@CordaSerializable @CordaSerializable
data class Handshake<out T>(val payload: T, val publicKey: CompositeKey) data class Handshake<out T>(val payload: T, val publicKey: PublicKey)
@CordaSerializable @CordaSerializable
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySigs: List<DigitalSignature.WithKey>) class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySigs: List<DigitalSignature.WithKey>)
@ -92,7 +93,7 @@ object TwoPartyDealFlow {
progressTracker.currentStep = AWAITING_PROPOSAL progressTracker.currentStep = AWAITING_PROPOSAL
// Make the first message we'll send to kick off the flow. // Make the first message we'll send to kick off the flow.
val hello = Handshake(payload, myKeyPair.public.composite) val hello = Handshake(payload, myKeyPair.public)
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello) val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
return maybeSTX return maybeSTX
@ -106,7 +107,7 @@ object TwoPartyDealFlow {
progressTracker.nextStep() progressTracker.nextStep()
// Check that the tx proposed by the buyer is valid. // Check that the tx proposed by the buyer is valid.
val wtx: WireTransaction = stx.verifySignatures(myKeyPair.public.composite, notaryNode.notaryIdentity.owningKey) val wtx: WireTransaction = stx.verifySignatures(myKeyPair.public, notaryNode.notaryIdentity.owningKey)
logger.trace { "Received partially signed transaction: ${stx.id}" } logger.trace { "Received partially signed transaction: ${stx.id}" }
checkDependencies(stx) checkDependencies(stx)
@ -253,9 +254,9 @@ object TwoPartyDealFlow {
return sendAndReceive<SignaturesFromPrimary>(otherParty, stx).unwrap { it } return sendAndReceive<SignaturesFromPrimary>(otherParty, stx).unwrap { it }
} }
private fun signWithOurKeys(signingPubKeys: List<CompositeKey>, ptx: TransactionBuilder): SignedTransaction { private fun signWithOurKeys(signingPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
// Now sign the transaction with whatever keys we need to move the cash. // Now sign the transaction with whatever keys we need to move the cash.
for (publicKey in signingPubKeys.keys) { for (publicKey in signingPubKeys.expandedCompositeKeys) {
val privateKey = serviceHub.keyManagementService.toPrivate(publicKey) val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
ptx.signWith(KeyPair(publicKey, privateKey)) ptx.signWith(KeyPair(publicKey, privateKey))
} }
@ -264,7 +265,7 @@ object TwoPartyDealFlow {
} }
@Suspendable protected abstract fun validateHandshake(handshake: Handshake<U>): Handshake<U> @Suspendable protected abstract fun validateHandshake(handshake: Handshake<U>): Handshake<U>
@Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<CompositeKey>> @Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<PublicKey>>
} }
@CordaSerializable @CordaSerializable
@ -297,7 +298,7 @@ object TwoPartyDealFlow {
return handshake.copy(payload = autoOffer.copy(dealBeingOffered = deal)) return handshake.copy(payload = autoOffer.copy(dealBeingOffered = deal))
} }
override fun assembleSharedTX(handshake: Handshake<AutoOffer>): Pair<TransactionBuilder, List<CompositeKey>> { override fun assembleSharedTX(handshake: Handshake<AutoOffer>): Pair<TransactionBuilder, List<PublicKey>> {
val deal = handshake.payload.dealBeingOffered val deal = handshake.payload.dealBeingOffered
val ptx = deal.generateAgreement(handshake.payload.notary) val ptx = deal.generateAgreement(handshake.payload.notary)

View File

@ -1,12 +1,11 @@
package net.corda.flows package net.corda.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import java.security.PublicKey
import java.security.cert.Certificate import java.security.cert.Certificate
object TxKeyFlowUtilities { object TxKeyFlowUtilities {
@ -15,7 +14,7 @@ object TxKeyFlowUtilities {
* process. * process.
*/ */
@Suspendable @Suspendable
fun receiveKey(flow: FlowLogic<*>, otherSide: Party): Pair<CompositeKey, Certificate?> { fun receiveKey(flow: FlowLogic<*>, otherSide: Party): Pair<PublicKey, Certificate?> {
val untrustedKey = flow.receive<ProvidedTransactionKey>(otherSide) val untrustedKey = flow.receive<ProvidedTransactionKey>(otherSide)
return untrustedKey.unwrap { return untrustedKey.unwrap {
// TODO: Verify the certificate connects the given key to the counterparty, once we have certificates // TODO: Verify the certificate connects the given key to the counterparty, once we have certificates
@ -29,8 +28,8 @@ object TxKeyFlowUtilities {
* a transaction with the counterparty, in order to avoid a DoS risk. * a transaction with the counterparty, in order to avoid a DoS risk.
*/ */
@Suspendable @Suspendable
fun provideKey(flow: FlowLogic<*>, otherSide: Party): CompositeKey { fun provideKey(flow: FlowLogic<*>, otherSide: Party): PublicKey {
val key = flow.serviceHub.keyManagementService.freshKey().public.composite val key = flow.serviceHub.keyManagementService.freshKey().public
// TODO: Generate and sign certificate for the key, once we have signing support for composite keys // TODO: Generate and sign certificate for the key, once we have signing support for composite keys
// (in this case the legal identity key) // (in this case the legal identity key)
flow.send(otherSide, ProvidedTransactionKey(key, null)) flow.send(otherSide, ProvidedTransactionKey(key, null))
@ -38,5 +37,5 @@ object TxKeyFlowUtilities {
} }
@CordaSerializable @CordaSerializable
data class ProvidedTransactionKey(val key: CompositeKey, val certificate: Certificate?) data class ProvidedTransactionKey(val key: PublicKey, val certificate: Certificate?)
} }

View File

@ -1,7 +1,6 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.utilities.DUMMY_PUBKEY_1 import net.corda.core.utilities.DUMMY_PUBKEY_1
import net.corda.core.utilities.DUMMY_PUBKEY_2 import net.corda.core.utilities.DUMMY_PUBKEY_2
@ -9,6 +8,7 @@ import net.corda.testing.MEGA_CORP
import net.corda.testing.ledger import net.corda.testing.ledger
import net.corda.testing.transaction import net.corda.testing.transaction
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
@ -40,7 +40,7 @@ class TransactionEncumbranceTests {
data class State( data class State(
val validFrom: Instant val validFrom: Instant
) : ContractState { ) : ContractState {
override val participants: List<CompositeKey> = emptyList() override val participants: List<PublicKey> = emptyList()
override val contract: Contract = TEST_TIMELOCK_ID override val contract: Contract = TEST_TIMELOCK_ID
} }
} }

View File

@ -1,6 +1,5 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.composite
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
@ -32,7 +31,7 @@ class TransactionGraphSearchTests {
fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage { fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage {
val originTx = TransactionType.General.Builder(DUMMY_NOTARY).apply { val originTx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
addOutputState(DummyState(random31BitValue())) addOutputState(DummyState(random31BitValue()))
addCommand(command, signer.public.composite) addCommand(command, signer.public)
signWith(signer) signWith(signer)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction(false) }.toSignedTransaction(false)

View File

@ -1,9 +1,10 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
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
import net.corda.core.crypto.composite import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.signWithECDSA import net.corda.core.crypto.signWithECDSA
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
@ -22,6 +23,48 @@ import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
class TransactionTests { class TransactionTests {
private fun makeSigned(wtx: WireTransaction, vararg keys: KeyPair): SignedTransaction {
val bytes: SerializedBytes<WireTransaction> = wtx.serialized
return SignedTransaction(bytes, keys.map { it.signWithECDSA(wtx.id.bytes) })
}
@Test
fun `signed transaction missing signatures - CompositeKey`() {
val ak = generateKeyPair()
val bk = generateKeyPair()
val ck = generateKeyPair()
val apub = ak.public
val bpub = bk.public
val cpub = ck.public
val c1 = CompositeKey.Builder().addKeys(apub, bpub).build(2)
val compKey = CompositeKey.Builder().addKeys(c1, cpub).build(1)
val wtx = WireTransaction(
inputs = listOf(StateRef(SecureHash.randomSHA256(), 0)),
attachments = emptyList(),
outputs = emptyList(),
commands = emptyList(),
notary = DUMMY_NOTARY,
signers = listOf(compKey, DUMMY_KEY_1.public, DUMMY_KEY_2.public),
type = TransactionType.General,
timestamp = null
)
assertEquals(
setOf(compKey, DUMMY_KEY_2.public),
assertFailsWith<SignedTransaction.SignaturesMissingException> { makeSigned(wtx, DUMMY_KEY_1).verifySignatures() }.missing
)
assertEquals(
setOf(compKey, DUMMY_KEY_2.public),
assertFailsWith<SignedTransaction.SignaturesMissingException> { makeSigned(wtx, DUMMY_KEY_1, ak).verifySignatures() }.missing
)
makeSigned(wtx, DUMMY_KEY_1, DUMMY_KEY_2, ak, bk).verifySignatures()
makeSigned(wtx, DUMMY_KEY_1, DUMMY_KEY_2, ck).verifySignatures()
makeSigned(wtx, DUMMY_KEY_1, DUMMY_KEY_2, ak, bk, ck).verifySignatures()
makeSigned(wtx, DUMMY_KEY_1, DUMMY_KEY_2, ak).verifySignatures(compKey)
makeSigned(wtx, DUMMY_KEY_1, ak).verifySignatures(compKey, DUMMY_KEY_2.public) // Mixed allowed to be missing.
}
@Test @Test
fun `signed transaction missing signatures`() { fun `signed transaction missing signatures`() {
val wtx = WireTransaction( val wtx = WireTransaction(
@ -30,31 +73,29 @@ class TransactionTests {
outputs = emptyList(), outputs = emptyList(),
commands = emptyList(), commands = emptyList(),
notary = DUMMY_NOTARY, notary = DUMMY_NOTARY,
signers = listOf(DUMMY_KEY_1.public.composite, DUMMY_KEY_2.public.composite), signers = listOf(DUMMY_KEY_1.public, DUMMY_KEY_2.public),
type = TransactionType.General, type = TransactionType.General,
timestamp = null timestamp = null
) )
val bytes: SerializedBytes<WireTransaction> = wtx.serialized assertFailsWith<IllegalArgumentException> { makeSigned(wtx).verifySignatures() }
fun make(vararg keys: KeyPair) = SignedTransaction(bytes, keys.map { it.signWithECDSA(wtx.id.bytes) })
assertFailsWith<IllegalArgumentException> { make().verifySignatures() }
assertEquals( assertEquals(
setOf(DUMMY_KEY_1.public.composite), setOf(DUMMY_KEY_1.public),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_2).verifySignatures() }.missing assertFailsWith<SignedTransaction.SignaturesMissingException> { makeSigned(wtx, DUMMY_KEY_2).verifySignatures() }.missing
) )
assertEquals( assertEquals(
setOf(DUMMY_KEY_2.public.composite), setOf(DUMMY_KEY_2.public),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_1).verifySignatures() }.missing assertFailsWith<SignedTransaction.SignaturesMissingException> { makeSigned(wtx, DUMMY_KEY_1).verifySignatures() }.missing
) )
assertEquals( assertEquals(
setOf(DUMMY_KEY_2.public.composite), setOf(DUMMY_KEY_2.public),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public.composite) }.missing assertFailsWith<SignedTransaction.SignaturesMissingException> { makeSigned(wtx, DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public) }.missing
) )
make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public.composite) makeSigned(wtx, DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public)
make(DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public.composite) makeSigned(wtx, DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public)
make(DUMMY_KEY_1, DUMMY_KEY_2).verifySignatures() makeSigned(wtx, DUMMY_KEY_1, DUMMY_KEY_2).verifySignatures()
} }
@Test @Test
@ -65,7 +106,7 @@ class TransactionTests {
val commands = emptyList<AuthenticatedObject<CommandData>>() val commands = emptyList<AuthenticatedObject<CommandData>>()
val attachments = emptyList<Attachment>() val attachments = emptyList<Attachment>()
val id = SecureHash.randomSHA256() val id = SecureHash.randomSHA256()
val signers = listOf(DUMMY_NOTARY_KEY.public.composite) val signers = listOf(DUMMY_NOTARY_KEY.public)
val timestamp: Timestamp? = null val timestamp: Timestamp? = null
val transaction: LedgerTransaction = LedgerTransaction( val transaction: LedgerTransaction = LedgerTransaction(
inputs, inputs,
@ -92,7 +133,7 @@ class TransactionTests {
val commands = emptyList<AuthenticatedObject<CommandData>>() val commands = emptyList<AuthenticatedObject<CommandData>>()
val attachments = emptyList<Attachment>() val attachments = emptyList<Attachment>()
val id = SecureHash.randomSHA256() val id = SecureHash.randomSHA256()
val signers = listOf(DUMMY_NOTARY_KEY.public.composite) val signers = listOf(DUMMY_NOTARY_KEY.public)
val timestamp: Timestamp? = null val timestamp: Timestamp? = null
val transaction: LedgerTransaction = LedgerTransaction( val transaction: LedgerTransaction = LedgerTransaction(
inputs, inputs,
@ -119,7 +160,7 @@ class TransactionTests {
val commands = emptyList<AuthenticatedObject<CommandData>>() val commands = emptyList<AuthenticatedObject<CommandData>>()
val attachments = emptyList<Attachment>() val attachments = emptyList<Attachment>()
val id = SecureHash.randomSHA256() val id = SecureHash.randomSHA256()
val signers = listOf(DUMMY_NOTARY_KEY.public.composite) val signers = listOf(DUMMY_NOTARY_KEY.public)
val timestamp: Timestamp? = null val timestamp: Timestamp? = null
val transaction: LedgerTransaction = LedgerTransaction( val transaction: LedgerTransaction = LedgerTransaction(
inputs, inputs,

View File

@ -3,6 +3,8 @@ package net.corda.core.crypto
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class CompositeKeyTests { class CompositeKeyTests {
@ -10,9 +12,9 @@ class CompositeKeyTests {
val bobKey = generateKeyPair() val bobKey = generateKeyPair()
val charlieKey = generateKeyPair() val charlieKey = generateKeyPair()
val alicePublicKey = CompositeKey.Leaf(aliceKey.public) val alicePublicKey = aliceKey.public
val bobPublicKey = CompositeKey.Leaf(bobKey.public) val bobPublicKey = bobKey.public
val charliePublicKey = CompositeKey.Leaf(charlieKey.public) val charliePublicKey = charlieKey.public
val message = OpaqueBytes("Transaction".toByteArray()) val message = OpaqueBytes("Transaction".toByteArray())
@ -54,8 +56,35 @@ class CompositeKeyTests {
val aliceAndBobOrCharlie = CompositeKey.Builder().addKeys(aliceAndBob, charliePublicKey).build(threshold = 1) val aliceAndBobOrCharlie = CompositeKey.Builder().addKeys(aliceAndBob, charliePublicKey).build(threshold = 1)
val encoded = aliceAndBobOrCharlie.toBase58String() val encoded = aliceAndBobOrCharlie.toBase58String()
val decoded = CompositeKey.parseFromBase58(encoded) val decoded = parsePublicKeyBase58(encoded)
assertEquals(decoded, aliceAndBobOrCharlie) assertEquals(decoded, aliceAndBobOrCharlie)
} }
@Test
fun `tree canonical form`() {
assertEquals(CompositeKey.Builder().addKeys(alicePublicKey).build(), alicePublicKey)
val node1 = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey).build(1) // threshold = 1
val node2 = CompositeKey.Builder().addKeys(alicePublicKey, bobPublicKey).build(2) // threshold = 2
assertFalse(node2.isFulfilledBy(alicePublicKey))
// Ordering by weight.
val tree1 = CompositeKey.Builder().addKey(node1, 13).addKey(node2, 27).build()
val tree2 = CompositeKey.Builder().addKey(node2, 27).addKey(node1, 13).build()
assertEquals(tree1, tree2)
assertEquals(tree1.hashCode(), tree2.hashCode())
// Ordering by node, weights the same.
val tree3 = CompositeKey.Builder().addKeys(node1, node2).build()
val tree4 = CompositeKey.Builder().addKeys(node2, node1).build()
assertEquals(tree3, tree4)
assertEquals(tree3.hashCode(), tree4.hashCode())
// Duplicate node cases.
val tree5 = CompositeKey.Builder().addKey(node1, 3).addKey(node1, 14).build()
val tree6 = CompositeKey.Builder().addKey(node1, 14).addKey(node1, 3).build()
assertEquals(tree5, tree6)
// Chain of single nodes should throw.
assertEquals(CompositeKey.Builder().addKeys(tree1).build(), tree1)
}
} }

View File

@ -15,6 +15,7 @@ import net.corda.testing.MEGA_CORP
import net.corda.testing.MEGA_CORP_PUBKEY import net.corda.testing.MEGA_CORP_PUBKEY
import net.corda.testing.ledger import net.corda.testing.ledger
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import kotlin.test.* import kotlin.test.*
class PartialMerkleTreeTest { class PartialMerkleTreeTest {
@ -99,7 +100,7 @@ class PartialMerkleTreeTest {
is TransactionState<*> -> elem.data.participants[0].keys == DUMMY_PUBKEY_1.keys is TransactionState<*> -> elem.data.participants[0].keys == DUMMY_PUBKEY_1.keys
is Command -> MEGA_CORP_PUBKEY in elem.signers is Command -> MEGA_CORP_PUBKEY in elem.signers
is Timestamp -> true is Timestamp -> true
is CompositeKey -> elem == MEGA_CORP_PUBKEY is PublicKey -> elem == MEGA_CORP_PUBKEY
else -> false else -> false
} }
} }

View File

@ -8,8 +8,8 @@ import kotlin.test.assertNotEquals
class PartyTest { class PartyTest {
@Test @Test
fun `equality`() { fun `equality`() {
val key = entropyToKeyPair(BigInteger.valueOf(20170207L)).public.composite val key = entropyToKeyPair(BigInteger.valueOf(20170207L)).public
val differentKey = entropyToKeyPair(BigInteger.valueOf(7201702L)).public.composite val differentKey = entropyToKeyPair(BigInteger.valueOf(7201702L)).public
val anonymousParty = AnonymousParty(key) val anonymousParty = AnonymousParty(key)
val party = Party("test key", key) val party = Party("test key", key)
assertEquals<AbstractParty>(party, anonymousParty) assertEquals<AbstractParty>(party, anonymousParty)

View File

@ -2,7 +2,6 @@ package net.corda.core.flows
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.* import net.corda.core.contracts.*
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
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
@ -27,6 +26,7 @@ import java.util.concurrent.ExecutionException
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
import java.security.*
class ContractUpgradeFlowTest { class ContractUpgradeFlowTest {
lateinit var mockNet: MockNetwork lateinit var mockNet: MockNetwork
@ -175,15 +175,15 @@ class ContractUpgradeFlowTest {
class CashV2 : UpgradedContract<Cash.State, CashV2.State> { class CashV2 : UpgradedContract<Cash.State, CashV2.State> {
override val legacyContract = Cash::class.java override val legacyContract = Cash::class.java
data class State(override val amount: Amount<Issued<Currency>>, val owners: List<CompositeKey>) : FungibleAsset<Currency> { data class State(override val amount: Amount<Issued<Currency>>, val owners: List<PublicKey>) : FungibleAsset<Currency> {
override val owner: CompositeKey = owners.first() override val owner: PublicKey = owners.first()
override val exitKeys = (owners + amount.token.issuer.party.owningKey).toSet() override val exitKeys = (owners + amount.token.issuer.party.owningKey).toSet()
override val contract = CashV2() override val contract = CashV2()
override val participants = owners override val participants = owners
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: CompositeKey) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner)) override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKey) = copy(amount = amount.copy(newAmount.quantity), owners = listOf(newOwner))
override fun toString() = "${Emoji.bagOfCash}New Cash($amount at ${amount.token.issuer} owned by $owner)" override fun toString() = "${Emoji.bagOfCash}New Cash($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: CompositeKey) = Pair(Cash.Commands.Move(), copy(owners = listOf(newOwner))) override fun withNewOwner(newOwner: PublicKey) = Pair(Cash.Commands.Move(), copy(owners = listOf(newOwner)))
} }
override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner)) override fun upgrade(state: Cash.State) = CashV2.State(state.amount.times(1000), listOf(state.owner))

View File

@ -1,11 +1,11 @@
package net.corda.core.flows package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.flows.TxKeyFlowUtilities import net.corda.flows.TxKeyFlowUtilities
import java.security.PublicKey
import java.security.cert.Certificate import java.security.cert.Certificate
/** /**
@ -19,7 +19,7 @@ object TxKeyFlow {
} }
class Requester(val otherSide: Party, class Requester(val otherSide: Party,
override val progressTracker: ProgressTracker) : FlowLogic<Pair<CompositeKey, Certificate?>>() { override val progressTracker: ProgressTracker) : FlowLogic<Pair<PublicKey, Certificate?>>() {
constructor(otherSide: Party) : this(otherSide, tracker()) constructor(otherSide: Party) : this(otherSide, tracker())
companion object { companion object {
@ -29,7 +29,7 @@ object TxKeyFlow {
} }
@Suspendable @Suspendable
override fun call(): Pair<CompositeKey, Certificate?> { override fun call(): Pair<PublicKey, Certificate?> {
progressTracker.currentStep = AWAITING_KEY progressTracker.currentStep = AWAITING_KEY
return TxKeyFlowUtilities.receiveKey(this, otherSide) return TxKeyFlowUtilities.receiveKey(this, otherSide)
} }
@ -40,7 +40,7 @@ object TxKeyFlow {
* counterparty and as the result from the flow. * counterparty and as the result from the flow.
*/ */
class Provider(val otherSide: Party, class Provider(val otherSide: Party,
override val progressTracker: ProgressTracker) : FlowLogic<CompositeKey>() { override val progressTracker: ProgressTracker) : FlowLogic<PublicKey>() {
constructor(otherSide: Party) : this(otherSide, tracker()) constructor(otherSide: Party) : this(otherSide, tracker())
companion object { companion object {
@ -50,7 +50,7 @@ object TxKeyFlow {
} }
@Suspendable @Suspendable
override fun call(): CompositeKey { override fun call(): PublicKey {
progressTracker.currentStep == SENDING_KEY progressTracker.currentStep == SENDING_KEY
return TxKeyFlowUtilities.provideKey(this, otherSide) return TxKeyFlowUtilities.provideKey(this, otherSide)
} }

View File

@ -1,6 +1,5 @@
package net.corda.core.flows package net.corda.core.flows
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.testing.ALICE import net.corda.testing.ALICE
@ -9,6 +8,7 @@ import net.corda.testing.MOCK_IDENTITY_SERVICE
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
class TxKeyFlowUtilitiesTests { class TxKeyFlowUtilitiesTests {
@ -36,7 +36,7 @@ class TxKeyFlowUtilitiesTests {
val requesterFlow = aliceNode.services.startFlow(TxKeyFlow.Requester(bobKey)) val requesterFlow = aliceNode.services.startFlow(TxKeyFlow.Requester(bobKey))
// Get the results // Get the results
val actual: CompositeKey = requesterFlow.resultFuture.get().first val actual: PublicKey = requesterFlow.resultFuture.get().first
assertNotNull(actual) assertNotNull(actual)
} }
} }

View File

@ -2,7 +2,6 @@ package net.corda.core.node
import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Kryo
import net.corda.core.contracts.* import net.corda.core.contracts.*
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
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
@ -18,6 +17,7 @@ import org.junit.Test
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.net.URLClassLoader import java.net.URLClassLoader
import java.security.PublicKey
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -39,7 +39,7 @@ class AttachmentClassLoaderTests {
class AttachmentDummyContract : Contract { class AttachmentDummyContract : Contract {
data class State(val magicNumber: Int = 0) : ContractState { data class State(val magicNumber: Int = 0) : ContractState {
override val contract = ATTACHMENT_TEST_PROGRAM_ID override val contract = ATTACHMENT_TEST_PROGRAM_ID
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = listOf() get() = listOf()
} }

View File

@ -1,11 +1,11 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -20,7 +20,7 @@ class VaultUpdateTests {
} }
private class DummyState : ContractState { private class DummyState : ContractState {
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = emptyList() get() = emptyList()
override val contract = VaultUpdateTests.DummyContract override val contract = VaultUpdateTests.DummyContract
} }

View File

@ -1,9 +1,7 @@
package net.corda.core.serialization package net.corda.core.serialization
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.composite
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.* import net.corda.core.utilities.*
@ -11,6 +9,7 @@ import net.corda.testing.MINI_CORP
import net.corda.testing.generateStateRef import net.corda.testing.generateStateRef
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.* import java.util.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -28,12 +27,12 @@ class TransactionSerializationTests {
data class State( data class State(
val deposit: PartyAndReference, val deposit: PartyAndReference,
val amount: Amount<Currency>, val amount: Amount<Currency>,
override val owner: CompositeKey) : OwnableState { override val owner: PublicKey) : OwnableState {
override val contract: Contract = TEST_PROGRAM_ID override val contract: Contract = TEST_PROGRAM_ID
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -47,7 +46,7 @@ class TransactionSerializationTests {
val fakeStateRef = generateStateRef() val fakeStateRef = generateStateRef()
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY), fakeStateRef) val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY), fakeStateRef)
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY) val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY)
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public.composite), DUMMY_NOTARY) val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public), DUMMY_NOTARY)
lateinit var tx: TransactionBuilder lateinit var tx: TransactionBuilder
@ -55,7 +54,7 @@ class TransactionSerializationTests {
@Before @Before
fun setup() { fun setup() {
tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems( tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems(
inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(DUMMY_KEY_1.public.composite)) inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(DUMMY_KEY_1.public))
) )
} }
@ -94,7 +93,7 @@ class TransactionSerializationTests {
// If the signature was replaced in transit, we don't like it. // If the signature was replaced in transit, we don't like it.
assertFailsWith(SignatureException::class) { assertFailsWith(SignatureException::class) {
val tx2 = TransactionType.General.Builder(DUMMY_NOTARY).withItems(inputState, outputState, changeState, val tx2 = TransactionType.General.Builder(DUMMY_NOTARY).withItems(inputState, outputState, changeState,
Command(TestCash.Commands.Move(), DUMMY_KEY_2.public.composite)) Command(TestCash.Commands.Move(), DUMMY_KEY_2.public))
tx2.signWith(DUMMY_NOTARY_KEY) tx2.signWith(DUMMY_NOTARY_KEY)
tx2.signWith(DUMMY_KEY_2) tx2.signWith(DUMMY_KEY_2)

View File

@ -33,27 +33,22 @@ class PrivateKeyGenerator : Generator<PrivateKey>(PrivateKey::class.java) {
} }
} }
// TODO add CompositeKeyGenerator that actually does something useful.
class PublicKeyGenerator : Generator<PublicKey>(PublicKey::class.java) { class PublicKeyGenerator : Generator<PublicKey>(PublicKey::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PublicKey { override fun generate(random: SourceOfRandomness, status: GenerationStatus): PublicKey {
return entropyToKeyPair(random.nextBigInteger(32)).public return entropyToKeyPair(random.nextBigInteger(32)).public
} }
} }
class CompositeKeyGenerator : Generator<CompositeKey>(CompositeKey::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): CompositeKey {
return entropyToKeyPair(random.nextBigInteger(32)).public.composite
}
}
class AnonymousPartyGenerator : Generator<AnonymousParty>(AnonymousParty::class.java) { class AnonymousPartyGenerator : Generator<AnonymousParty>(AnonymousParty::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): AnonymousParty { override fun generate(random: SourceOfRandomness, status: GenerationStatus): AnonymousParty {
return AnonymousParty(CompositeKeyGenerator().generate(random, status)) return AnonymousParty(PublicKeyGenerator().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), PublicKeyGenerator().generate(random, status))
} }
} }

View File

@ -86,35 +86,10 @@ Bank A and Bank B decided to upgrade the contract to ``DummyContractV2``
1. Developer will create a new contract extending the ``UpgradedContract`` class, and a new state object ``DummyContractV2.State`` referencing the new contract. 1. Developer will create a new contract extending the ``UpgradedContract`` class, and a new state object ``DummyContractV2.State`` referencing the new contract.
.. container:: codeset .. literalinclude:: /../../core/src/main/kotlin/net/corda/core/contracts/DummyContractV2.kt
:language: kotlin
.. sourcecode:: kotlin :start-after: DOCSTART 1
:end-before: DOCEND 1
class DummyContractV2 : UpgradedContract<DummyContract.State, DummyContractV2.State> {
override val legacyContract = DummyContract::class.java
data class State(val magicNumber: Int = 0, val owners: List<CompositeKey>) : ContractState {
override val contract = DUMMY_V2_PROGRAM_ID
override val participants: List<CompositeKey> = owners
}
interface Commands : CommandData {
class Create : TypeOnlyCommandData(), Commands
class Move : TypeOnlyCommandData(), Commands
}
override fun upgrade(state: DummyContract.State): DummyContractV2.State {
return DummyContractV2.State(state.magicNumber, state.participants)
}
override fun verify(tx: TransactionForContract) {
if (tx.commands.any { it.value is UpgradeCommand }) ContractUpgradeFlow.verify(tx)
// Other verifications.
}
// The "empty contract"
override val legalContractReference: SecureHash = SecureHash.sha256("")
}
2. Bank A will instruct its node to accept the contract upgrade to ``DummyContractV2`` for the contract state. 2. Bank A will instruct its node to accept the contract upgrade to ``DummyContractV2`` for the contract state.

View File

@ -62,7 +62,7 @@ data class TradeApprovalContract(override val legalContractReference: SecureHash
override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState { override val contract: TradeApprovalContract = TradeApprovalContract()) : LinearState {
val parties: List<Party> get() = listOf(source, counterparty) val parties: List<Party> get() = listOf(source, counterparty)
override val participants: List<CompositeKey> get() = parties.map { it.owningKey } override val participants: List<PublicKey> get() = parties.map { it.owningKey }
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { it.containsAny(ourKeys) } return participants.any { it.containsAny(ourKeys) }

View File

@ -121,7 +121,7 @@ each side.
data class SellerTradeInfo( data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>, val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>, val price: Amount<Currency>,
val sellerOwnerKey: CompositeKey val sellerOwnerKey: PublicKey
) )
data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey, data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey,

View File

@ -19,7 +19,7 @@ A number of interfaces then extend ``ContractState``, representing standardised
of state such as: of state such as:
``OwnableState`` ``OwnableState``
A state which has an owner (represented as a ``CompositeKey``, discussed later). Exposes the owner and a function A state which has an owner (represented as a ``PublicKey`` which can be a ``CompositeKey``, discussed later). Exposes the owner and a function
for replacing the owner e.g. when an asset is sold. for replacing the owner e.g. when an asset is sold.
``SchedulableState`` ``SchedulableState``
@ -90,7 +90,7 @@ keys under their control.
Parties can be represented either in full (including name) or pseudonymously, using the ``Party`` or ``AnonymousParty`` Parties can be represented either in full (including name) or pseudonymously, using the ``Party`` or ``AnonymousParty``
classes respectively. For example, in a transaction sent to your node as part of a chain of custody it is important you classes respectively. For example, in a transaction sent to your node as part of a chain of custody it is important you
can convince yourself of the transaction's validity, but equally important that you don't learn anything about who was can convince yourself of the transaction's validity, but equally important that you don't learn anything about who was
involved in that transaction. In these cases ``AnonymousParty`` should be used, which contains a composite public key involved in that transaction. In these cases ``AnonymousParty`` should be used, which contains a public key (may be a composite key)
without any identifying information about who owns it. In contrast, for internal processing where extended details of without any identifying information about who owns it. In contrast, for internal processing where extended details of
a party are required, the ``Party`` class should be used. The identity service provides functionality for resolving a party are required, the ``Party`` class should be used. The identity service provides functionality for resolving
anonymous parties to full parties. anonymous parties to full parties.

View File

@ -2,11 +2,11 @@ package net.corda.contracts.universal
import net.corda.core.contracts.BusinessCalendar import net.corda.core.contracts.BusinessCalendar
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import java.lang.reflect.Type import java.lang.reflect.Type
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
@ -153,7 +153,7 @@ operator fun Perceivable<BigDecimal>.div(n: Double) = PerceivableOperation(this,
operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n)) operator fun Perceivable<Int>.plus(n: Int) = PerceivableOperation(this, Operation.PLUS, const(n))
operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n)) operator fun Perceivable<Int>.minus(n: Int) = PerceivableOperation(this, Operation.MINUS, const(n))
data class TerminalEvent(val reference: Party, val source: CompositeKey) : Perceivable<Boolean> data class TerminalEvent(val reference: Party, val source: PublicKey) : Perceivable<Boolean>
// todo: holidays // todo: holidays
data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String, data class Interest(val amount: Perceivable<BigDecimal>, val dayCountConvention: String,

View File

@ -1,8 +1,8 @@
package net.corda.contracts.universal package net.corda.contracts.universal
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.security.PublicKey
import java.time.Instant import java.time.Instant
private class PrettyPrint(arr : Arrangement) { private class PrettyPrint(arr : Arrangement) {
@ -41,7 +41,7 @@ private class PrettyPrint(arr : Arrangement) {
return rv return rv
} }
val partyMap = mutableMapOf<CompositeKey, String>() val partyMap = mutableMapOf<PublicKey, String>()
val usedPartyNames = mutableSetOf<String>() val usedPartyNames = mutableSetOf<String>()
fun createPartyName(party : Party) : String fun createPartyName(party : Party) : String

View File

@ -1,17 +1,17 @@
package net.corda.contracts.universal package net.corda.contracts.universal
import net.corda.core.contracts.* import net.corda.core.contracts.*
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
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey
import java.time.Instant import java.time.Instant
val UNIVERSAL_PROGRAM_ID = UniversalContract() val UNIVERSAL_PROGRAM_ID = UniversalContract()
class UniversalContract : Contract { class UniversalContract : Contract {
data class State(override val participants: List<CompositeKey>, data class State(override val participants: List<PublicKey>,
val details: Arrangement) : ContractState { val details: Arrangement) : ContractState {
override val contract = UNIVERSAL_PROGRAM_ID override val contract = UNIVERSAL_PROGRAM_ID
} }
@ -316,7 +316,7 @@ class UniversalContract : Contract {
override val legalContractReference: SecureHash override val legalContractReference: SecureHash
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: CompositeKey) { fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKey) {
check(tx.inputStates().isEmpty()) check(tx.inputStates().isEmpty())
tx.addOutputState(State(listOf(notary), arrangement)) tx.addOutputState(State(listOf(notary), arrangement))
tx.addCommand(Commands.Issue(), at.party.owningKey) tx.addCommand(Commands.Issue(), at.party.owningKey)

View File

@ -3,8 +3,8 @@ package net.corda.contracts.universal
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import com.google.common.collect.Sets import com.google.common.collect.Sets
import net.corda.core.contracts.Frequency import net.corda.core.contracts.Frequency
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
@ -23,20 +23,20 @@ private fun signingParties(perceivable: Perceivable<Boolean>) : ImmutableSet<Par
else -> throw IllegalArgumentException("signingParties " + perceivable) else -> throw IllegalArgumentException("signingParties " + perceivable)
} }
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<CompositeKey> = private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> =
when (arrangement) { when (arrangement) {
is Zero -> ImmutableSet.of<CompositeKey>() is Zero -> ImmutableSet.of<PublicKey>()
is Obligation -> ImmutableSet.of(arrangement.from.owningKey) is Obligation -> ImmutableSet.of(arrangement.from.owningKey)
is And -> is And ->
arrangement.arrangements.fold(ImmutableSet.builder<CompositeKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
is Actions -> is Actions ->
arrangement.actions.fold(ImmutableSet.builder<CompositeKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
is RollOut -> liablePartiesVisitor(arrangement.template) is RollOut -> liablePartiesVisitor(arrangement.template)
is Continuation -> ImmutableSet.of<CompositeKey>() is Continuation -> ImmutableSet.of<PublicKey>()
else -> throw IllegalArgumentException("liableParties " + arrangement) else -> throw IllegalArgumentException("liableParties " + arrangement)
} }
private fun liablePartiesVisitor(action: Action): ImmutableSet<CompositeKey> { private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKey> {
val actors = signingParties(action.condition) val actors = signingParties(action.condition)
return if (actors.size != 1) return if (actors.size != 1)
liablePartiesVisitor(action.arrangement) liablePartiesVisitor(action.arrangement)
@ -45,7 +45,7 @@ private fun liablePartiesVisitor(action: Action): ImmutableSet<CompositeKey> {
} }
/** Returns list of potentially liable parties for a given contract */ /** Returns list of potentially liable parties for a given contract */
fun liableParties(contract: Arrangement): Set<CompositeKey> = liablePartiesVisitor(contract) fun liableParties(contract: Arrangement): Set<PublicKey> = liablePartiesVisitor(contract)
private fun involvedPartiesVisitor(action: Action): Set<Party> = private fun involvedPartiesVisitor(action: Action): Set<Party> =
Sets.union(involvedPartiesVisitor(action.arrangement), signingParties(action.condition)).immutableCopy() Sets.union(involvedPartiesVisitor(action.arrangement), signingParties(action.condition)).immutableCopy()

View File

@ -1,7 +1,6 @@
package net.corda.contracts.universal package net.corda.contracts.universal
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import org.junit.Test import org.junit.Test
import java.util.* import java.util.*
@ -11,7 +10,7 @@ val acmeCorp = Party("ACME Corporation", generateKeyPair().public)
val highStreetBank = Party("High Street Bank", generateKeyPair().public) val highStreetBank = Party("High Street Bank", generateKeyPair().public)
val momAndPop = Party("Mom and Pop", generateKeyPair().public) val momAndPop = Party("Mom and Pop", generateKeyPair().public)
val acmeCorporationHasDefaulted = TerminalEvent(acmeCorp, generateKeyPair().public.composite) val acmeCorporationHasDefaulted = TerminalEvent(acmeCorp, generateKeyPair().public)
// Currencies // Currencies

View File

@ -1,10 +1,10 @@
package net.corda.contracts.isolated package net.corda.contracts.isolated
import net.corda.core.contracts.* import net.corda.core.contracts.*
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
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
// The dummy contract doesn't do anything useful. It exists for testing purposes. // The dummy contract doesn't do anything useful. It exists for testing purposes.
@ -13,7 +13,7 @@ val ANOTHER_DUMMY_PROGRAM_ID = AnotherDummyContract()
class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor { class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor {
data class State(val magicNumber: Int = 0) : ContractState { data class State(val magicNumber: Int = 0) : ContractState {
override val contract = ANOTHER_DUMMY_PROGRAM_ID override val contract = ANOTHER_DUMMY_PROGRAM_ID
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = emptyList() get() = emptyList()
} }

View File

@ -1,8 +1,8 @@
package net.corda.contracts; package net.corda.contracts;
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.crypto.*;
import java.security.PublicKey;
import java.time.*; import java.time.*;
import java.util.*; import java.util.*;
@ -12,7 +12,7 @@ import java.util.*;
* ultimately either language can be used against a common test framework (and therefore can be used for real). * ultimately either language can be used against a common test framework (and therefore can be used for real).
*/ */
public interface ICommercialPaperState extends ContractState { public interface ICommercialPaperState extends ContractState {
ICommercialPaperState withOwner(CompositeKey newOwner); ICommercialPaperState withOwner(PublicKey newOwner);
ICommercialPaperState withFaceValue(Amount<Issued<Currency>> newFaceValue); ICommercialPaperState withFaceValue(Amount<Issued<Currency>> newFaceValue);

View File

@ -15,6 +15,7 @@ import org.jetbrains.annotations.*;
import java.time.*; import java.time.*;
import java.util.*; import java.util.*;
import java.util.stream.*; import java.util.stream.*;
import java.security.PublicKey;
import static kotlin.collections.CollectionsKt.*; import static kotlin.collections.CollectionsKt.*;
import static net.corda.core.contracts.ContractsDSL.*; import static net.corda.core.contracts.ContractsDSL.*;
@ -31,14 +32,14 @@ public class JavaCommercialPaper implements Contract {
@SuppressWarnings("unused") @SuppressWarnings("unused")
public static class State implements OwnableState, ICommercialPaperState { public static class State implements OwnableState, ICommercialPaperState {
private PartyAndReference issuance; private PartyAndReference issuance;
private CompositeKey owner; private PublicKey owner;
private Amount<Issued<Currency>> faceValue; private Amount<Issued<Currency>> faceValue;
private Instant maturityDate; private Instant maturityDate;
public State() { public State() {
} // For serialization } // For serialization
public State(PartyAndReference issuance, CompositeKey owner, Amount<Issued<Currency>> faceValue, public State(PartyAndReference issuance, PublicKey owner, Amount<Issued<Currency>> faceValue,
Instant maturityDate) { Instant maturityDate) {
this.issuance = issuance; this.issuance = issuance;
this.owner = owner; this.owner = owner;
@ -50,13 +51,13 @@ public class JavaCommercialPaper implements Contract {
return new State(this.issuance, this.owner, this.faceValue, this.maturityDate); return new State(this.issuance, this.owner, this.faceValue, this.maturityDate);
} }
public ICommercialPaperState withOwner(CompositeKey newOwner) { public ICommercialPaperState withOwner(PublicKey newOwner) {
return new State(this.issuance, newOwner, this.faceValue, this.maturityDate); return new State(this.issuance, newOwner, this.faceValue, this.maturityDate);
} }
@NotNull @NotNull
@Override @Override
public Pair<CommandData, OwnableState> withNewOwner(@NotNull CompositeKey newOwner) { public Pair<CommandData, OwnableState> withNewOwner(@NotNull PublicKey newOwner) {
return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate)); return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
} }
@ -73,7 +74,7 @@ public class JavaCommercialPaper implements Contract {
} }
@NotNull @NotNull
public CompositeKey getOwner() { public PublicKey getOwner() {
return owner; return owner;
} }
@ -114,12 +115,12 @@ public class JavaCommercialPaper implements Contract {
} }
public State withoutOwner() { public State withoutOwner() {
return new State(issuance, CryptoUtilities.getNullCompositeKey(), faceValue, maturityDate); return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate);
} }
@NotNull @NotNull
@Override @Override
public List<CompositeKey> getParticipants() { public List<PublicKey> getParticipants() {
return ImmutableList.of(this.owner); return ImmutableList.of(this.owner);
} }
} }
@ -316,7 +317,7 @@ public class JavaCommercialPaper implements Contract {
tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner())); tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner()));
} }
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, CompositeKey newOwner) { public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKey newOwner) {
tx.addInputState(paper); tx.addInputState(paper);
tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance())); tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary(), paper.getState().getEncumbrance()));
tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner())); tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner()));

View File

@ -7,9 +7,9 @@ import net.corda.core.contracts.clauses.AnyOf
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.contracts.clauses.GroupClauseVerifier import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.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
import net.corda.core.crypto.toBase58String
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.random63BitValue import net.corda.core.random63BitValue
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
@ -18,6 +18,7 @@ import net.corda.core.schemas.QueryableState
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import net.corda.schemas.CommercialPaperSchemaV1 import net.corda.schemas.CommercialPaperSchemaV1
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -59,22 +60,22 @@ class CommercialPaper : Contract {
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
override val owner: CompositeKey, override val owner: PublicKey,
val faceValue: Amount<Issued<Currency>>, val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant val maturityDate: Instant
) : OwnableState, QueryableState, ICommercialPaperState { ) : OwnableState, QueryableState, ICommercialPaperState {
override val contract = CP_PROGRAM_ID override val contract = CP_PROGRAM_ID
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = listOf(owner) get() = listOf(owner)
val token: Issued<Terms> val token: Issued<Terms>
get() = Issued(issuance, Terms(faceValue.token, maturityDate)) get() = Issued(issuance, Terms(faceValue.token, maturityDate))
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)" override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later, // Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
override fun withOwner(newOwner: CompositeKey): ICommercialPaperState = copy(owner = newOwner) override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner)
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue) override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate) override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
@ -199,7 +200,7 @@ class CommercialPaper : Contract {
/** /**
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper. * Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
*/ */
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: CompositeKey) { fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
tx.addInputState(paper) tx.addInputState(paper)
tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary)) tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary))
tx.addCommand(Commands.Move(), paper.state.data.owner) tx.addCommand(Commands.Move(), paper.state.data.owner)
@ -222,8 +223,8 @@ class CommercialPaper : Contract {
} }
} }
infix fun CommercialPaper.State.`owned by`(owner: CompositeKey) = copy(owner = owner) infix fun CommercialPaper.State.`owned by`(owner: PublicKey) = copy(owner = owner)
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary) infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
infix fun ICommercialPaperState.`owned by`(newOwner: CompositeKey) = withOwner(newOwner) infix fun ICommercialPaperState.`owned by`(newOwner: PublicKey) = withOwner(newOwner)

View File

@ -2,13 +2,13 @@ package net.corda.contracts
import net.corda.contracts.asset.sumCashBy import net.corda.contracts.asset.sumCashBy
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.NullPublicKey
import net.corda.core.crypto.NullCompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -26,19 +26,19 @@ class CommercialPaperLegacy : Contract {
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
override val owner: CompositeKey, override val owner: PublicKey,
val faceValue: Amount<Issued<Currency>>, val faceValue: Amount<Issued<Currency>>,
val maturityDate: Instant val maturityDate: Instant
) : OwnableState, ICommercialPaperState { ) : OwnableState, ICommercialPaperState {
override val contract = CP_LEGACY_PROGRAM_ID override val contract = CP_LEGACY_PROGRAM_ID
override val participants = listOf(owner) override val participants = listOf(owner)
fun withoutOwner() = copy(owner = NullCompositeKey) fun withoutOwner() = copy(owner = NullPublicKey)
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)" override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later, // Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
override fun withOwner(newOwner: CompositeKey): ICommercialPaperState = copy(owner = newOwner) override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner)
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue) override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate) override fun withMaturityDate(newMaturityDate: Instant): ICommercialPaperState = copy(maturityDate = newMaturityDate)
@ -117,7 +117,7 @@ class CommercialPaperLegacy : Contract {
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey)) return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
} }
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: CompositeKey) { fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
tx.addInputState(paper) tx.addInputState(paper)
tx.addOutputState(paper.state.data.withOwner(newOwner)) tx.addOutputState(paper.state.data.withOwner(newOwner))
tx.addCommand(Command(Commands.Move(), paper.state.data.owner)) tx.addCommand(Command(Commands.Move(), paper.state.data.owner))

View File

@ -17,6 +17,7 @@ import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import net.corda.schemas.CashSchemaV1 import net.corda.schemas.CashSchemaV1
import java.math.BigInteger import java.math.BigInteger
import java.security.PublicKey
import java.util.* import java.util.*
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -84,21 +85,21 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
override val amount: Amount<Issued<Currency>>, override val amount: Amount<Issued<Currency>>,
/** There must be a MoveCommand signed by this key to claim the amount. */ /** There must be a MoveCommand signed by this key to claim the amount. */
override val owner: CompositeKey override val owner: PublicKey
) : FungibleAsset<Currency>, QueryableState { ) : FungibleAsset<Currency>, QueryableState {
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: CompositeKey) constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKey)
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val exitKeys = setOf(owner, amount.token.issuer.party.owningKey) override val exitKeys = setOf(owner, amount.token.issuer.party.owningKey)
override val contract = CASH_PROGRAM_ID override val contract = CASH_PROGRAM_ID
override val participants = listOf(owner) override val participants = listOf(owner)
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: CompositeKey): FungibleAsset<Currency> override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKey): FungibleAsset<Currency>
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner) = copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)" override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
/** Object Relational Mapping support. */ /** Object Relational Mapping support. */
override fun generateMappedObject(schema: MappedSchema): PersistentState { override fun generateMappedObject(schema: MappedSchema): PersistentState {
@ -145,13 +146,13 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
/** /**
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey. * Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: CompositeKey, notary: Party) fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKey, notary: Party)
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary) = generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
/** /**
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: CompositeKey, notary: Party) { fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKey, notary: Party) {
check(tx.inputStates().isEmpty()) check(tx.inputStates().isEmpty())
check(tx.outputStates().map { it.data }.sumCashOrNull() == null) check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
require(amount.quantity > 0) require(amount.quantity > 0)
@ -160,7 +161,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
tx.addCommand(generateIssueCommand(), at.party.owningKey) tx.addCommand(generateIssueCommand(), at.party.owningKey)
} }
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: CompositeKey) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))
override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount) override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount)
@ -178,7 +179,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
* if there are none, or if any of the cash states cannot be added together (i.e. are * if there are none, or if any of the cash states cannot be added together (i.e. are
* different currencies or issuers). * different currencies or issuers).
*/ */
fun Iterable<ContractState>.sumCashBy(owner: CompositeKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow() fun Iterable<ContractState>.sumCashBy(owner: PublicKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
/** /**
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash * Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
@ -194,12 +195,12 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency) return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
} }
fun Cash.State.ownedBy(owner: CompositeKey) = copy(owner = owner) fun Cash.State.ownedBy(owner: PublicKey) = copy(owner = owner)
fun Cash.State.issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party.toAnonymous())))) fun Cash.State.issuedBy(party: AbstractParty) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party.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)))
infix fun Cash.State.`owned by`(owner: CompositeKey) = ownedBy(owner) infix fun Cash.State.`owned by`(owner: PublicKey) = ownedBy(owner)
infix fun Cash.State.`issued by`(party: AbstractParty) = issuedBy(party) infix fun Cash.State.`issued by`(party: AbstractParty) = issuedBy(party)
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit) infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit) infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)
@ -209,8 +210,8 @@ infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = wi
/** A randomly generated key. */ /** A randomly generated key. */
val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) } val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */ /** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public.composite).ref(1) } val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public).ref(1) }
/** An extension property that lets you write 100.DOLLARS.CASH */ /** An extension property that lets you write 100.DOLLARS.CASH */
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullCompositeKey) val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey)
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */ /** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullCompositeKey) val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKey)

View File

@ -7,12 +7,12 @@ import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.AnyOf import net.corda.core.contracts.clauses.AnyOf
import net.corda.core.contracts.clauses.GroupClauseVerifier import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.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
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.util.* import java.util.*
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -96,21 +96,21 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
override val amount: Amount<Issued<Commodity>>, override val amount: Amount<Issued<Commodity>>,
/** There must be a MoveCommand signed by this key to claim the amount */ /** There must be a MoveCommand signed by this key to claim the amount */
override val owner: CompositeKey override val owner: PublicKey
) : FungibleAsset<Commodity> { ) : FungibleAsset<Commodity> {
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: CompositeKey) constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKey)
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val contract = COMMODITY_PROGRAM_ID override val contract = COMMODITY_PROGRAM_ID
override val exitKeys = Collections.singleton(owner) override val exitKeys = Collections.singleton(owner)
override val participants = listOf(owner) override val participants = listOf(owner)
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: CompositeKey): FungibleAsset<Commodity> override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKey): FungibleAsset<Commodity>
= copy(amount = amount.copy(newAmount.quantity), owner = newOwner) = copy(amount = amount.copy(newAmount.quantity), owner = newOwner)
override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)" override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
} }
// Just for grouping // Just for grouping
@ -147,13 +147,13 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
/** /**
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey. * Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: CompositeKey, notary: Party) fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKey, notary: Party)
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary) = generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
/** /**
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey. * Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/ */
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: CompositeKey, notary: Party) { fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKey, notary: Party) {
check(tx.inputStates().isEmpty()) check(tx.inputStates().isEmpty())
check(tx.outputStates().map { it.data }.sumCashOrNull() == null) check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
val at = amount.token.issuer val at = amount.token.issuer
@ -162,7 +162,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
} }
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: CompositeKey) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKey)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))
override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount) override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount)

View File

@ -14,6 +14,7 @@ import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.TEST_TX_TIME import net.corda.core.utilities.TEST_TX_TIME
import net.corda.core.utilities.nonEmptySetOf import net.corda.core.utilities.nonEmptySetOf
import java.math.BigInteger import java.math.BigInteger
import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -273,23 +274,23 @@ class Obligation<P : Any> : Contract {
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: PublicKey
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> { ) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
constructor(lifecycle: Lifecycle = Lifecycle.NORMAL, constructor(lifecycle: Lifecycle = Lifecycle.NORMAL,
obligor: Party, obligor: Party,
template: Terms<P>, template: Terms<P>,
quantity: Long, quantity: Long,
beneficiary: CompositeKey) beneficiary: PublicKey)
: this(lifecycle, obligor.toAnonymous(), template, quantity, beneficiary) : 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<PublicKey> = setOf(beneficiary)
val dueBefore: Instant = template.dueBefore val dueBefore: Instant = template.dueBefore
override val participants: List<CompositeKey> = listOf(obligor.owningKey, beneficiary) override val participants: List<PublicKey> = listOf(obligor.owningKey, beneficiary)
override val owner: CompositeKey = beneficiary override val owner: PublicKey = beneficiary
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: CompositeKey): State<P> override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKey): State<P>
= copy(quantity = newAmount.quantity, beneficiary = newOwner) = copy(quantity = newAmount.quantity, beneficiary = newOwner)
override fun toString() = when (lifecycle) { override fun toString() = when (lifecycle) {
@ -322,7 +323,7 @@ class Obligation<P : Any> : Contract {
} }
} }
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Move(), copy(beneficiary = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(beneficiary = newOwner))
} }
// Just for grouping // Just for grouping
@ -428,7 +429,7 @@ class Obligation<P : Any> : Contract {
* and same parties involved). * and same parties involved).
*/ */
fun generateCloseOutNetting(tx: TransactionBuilder, fun generateCloseOutNetting(tx: TransactionBuilder,
signer: CompositeKey, signer: PublicKey,
vararg states: State<P>) { vararg states: State<P>) {
val netState = states.firstOrNull()?.bilateralNetState val netState = states.firstOrNull()?.bilateralNetState
@ -456,7 +457,7 @@ class Obligation<P : Any> : Contract {
*/ */
@Suppress("unused") @Suppress("unused")
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<Terms<P>>>, fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<Terms<P>>>,
assetStates: List<StateAndRef<Obligation.State<P>>>): CompositeKey assetStates: List<StateAndRef<Obligation.State<P>>>): PublicKey
= Clauses.ConserveAmount<P>().generateExit(tx, amountIssued, assetStates, = Clauses.ConserveAmount<P>().generateExit(tx, amountIssued, assetStates,
deriveState = { state, amount, owner -> state.copy(data = state.data.move(amount, owner)) }, deriveState = { state, amount, owner -> state.copy(data = state.data.move(amount, owner)) },
generateMoveCommand = { -> Commands.Move() }, generateMoveCommand = { -> Commands.Move() },
@ -470,7 +471,7 @@ class Obligation<P : Any> : Contract {
obligor: AbstractParty, obligor: AbstractParty,
issuanceDef: Terms<P>, issuanceDef: Terms<P>,
pennies: Long, pennies: Long,
beneficiary: CompositeKey, beneficiary: PublicKey,
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)
@ -486,7 +487,7 @@ class Obligation<P : Any> : 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, AnonymousParty>() val partyLookup = HashMap<PublicKey, 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.
@ -532,7 +533,7 @@ class Obligation<P : Any> : Contract {
// Produce a new set of states // Produce a new set of states
val groups = statesAndRefs.groupBy { it.state.data.amount.token } val groups = statesAndRefs.groupBy { it.state.data.amount.token }
for ((_, stateAndRefs) in groups) { for ((_, stateAndRefs) in groups) {
val partiesUsed = ArrayList<CompositeKey>() val partiesUsed = ArrayList<PublicKey>()
stateAndRefs.forEach { stateAndRef -> stateAndRefs.forEach { stateAndRef ->
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle) val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
tx.addInputState(stateAndRef) tx.addInputState(stateAndRef)
@ -577,7 +578,7 @@ class Obligation<P : Any> : Contract {
val template: Terms<P> = issuanceDef.product val template: Terms<P> = issuanceDef.product
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product) val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
var obligationRemaining: Amount<P> = obligationTotal var obligationRemaining: Amount<P> = obligationTotal
val assetSigners = HashSet<CompositeKey>() val assetSigners = HashSet<PublicKey>()
statesAndRefs.forEach { tx.addInputState(it) } statesAndRefs.forEach { tx.addInputState(it) }
@ -629,8 +630,8 @@ class Obligation<P : Any> : Contract {
* *
* @return a map of obligor/beneficiary pairs to the balance due. * @return a map of obligor/beneficiary pairs to the balance due.
*/ */
fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<CompositeKey, CompositeKey>, Amount<Obligation.Terms<P>>> { fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>> {
val balances = HashMap<Pair<CompositeKey, CompositeKey>, Amount<Obligation.Terms<P>>>() val balances = HashMap<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>>()
states.forEach { state -> states.forEach { state ->
val key = Pair(state.obligor.owningKey, state.beneficiary) val key = Pair(state.obligor.owningKey, state.beneficiary)
@ -644,8 +645,8 @@ fun <P : Any> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<O
/** /**
* Net off the amounts due between parties. * Net off the amounts due between parties.
*/ */
fun <P : Any> netAmountsDue(balances: Map<Pair<CompositeKey, CompositeKey>, Amount<P>>): Map<Pair<CompositeKey, CompositeKey>, Amount<P>> { fun <P : Any> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<Pair<PublicKey, PublicKey>, Amount<P>> {
val nettedBalances = HashMap<Pair<CompositeKey, CompositeKey>, Amount<P>>() val nettedBalances = HashMap<Pair<PublicKey, PublicKey>, Amount<P>>()
balances.forEach { balance -> balances.forEach { balance ->
val (obligor, beneficiary) = balance.key val (obligor, beneficiary) = balance.key
@ -669,8 +670,8 @@ fun <P : Any> netAmountsDue(balances: Map<Pair<CompositeKey, CompositeKey>, Amou
* @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being * @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being
* returned. * returned.
*/ */
fun <P : Any> sumAmountsDue(balances: Map<Pair<CompositeKey, CompositeKey>, Amount<P>>): Map<CompositeKey, Long> { fun <P : Any> sumAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<PublicKey, Long> {
val sum = HashMap<CompositeKey, Long>() val sum = HashMap<PublicKey, Long>()
// Fill the map with zeroes initially // Fill the map with zeroes initially
balances.keys.forEach { balances.keys.forEach {
@ -711,20 +712,20 @@ fun <P : Any> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<O
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef) = filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
infix fun <T : Any> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore)) infix fun <T : Any> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T : Any> Obligation.State<T>.between(parties: Pair<AbstractParty, CompositeKey>) = copy(obligor = parties.first.toAnonymous(), beneficiary = parties.second) infix fun <T : Any> Obligation.State<T>.between(parties: Pair<AbstractParty, PublicKey>) = copy(obligor = parties.first.toAnonymous(), beneficiary = parties.second)
infix fun <T : Any> Obligation.State<T>.`owned by`(owner: CompositeKey) = copy(beneficiary = owner) infix fun <T : Any> Obligation.State<T>.`owned by`(owner: PublicKey) = copy(beneficiary = owner)
infix fun <T : Any> Obligation.State<T>.`issued by`(party: AbstractParty) = copy(obligor = party.toAnonymous()) infix fun <T : Any> Obligation.State<T>.`issued by`(party: AbstractParty) = copy(obligor = party.toAnonymous())
// For Java users: // For Java users:
@Suppress("unused") fun <T : Any> Obligation.State<T>.ownedBy(owner: CompositeKey) = copy(beneficiary = owner) @Suppress("unused") fun <T : Any> Obligation.State<T>.ownedBy(owner: PublicKey) = copy(beneficiary = owner)
@Suppress("unused") fun <T : Any> Obligation.State<T>.issuedBy(party: AnonymousParty) = copy(obligor = party) @Suppress("unused") fun <T : Any> Obligation.State<T>.issuedBy(party: AnonymousParty) = copy(obligor = party)
/** 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)) }
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */ /** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
val DUMMY_OBLIGATION_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_OBLIGATION_ISSUER_KEY.public.composite) } val DUMMY_OBLIGATION_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_OBLIGATION_ISSUER_KEY.public) }
val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency> val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME) get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency> val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER.toAnonymous(), token.OBLIGATION_DEF, quantity, NullCompositeKey) get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER.toAnonymous(), token.OBLIGATION_DEF, quantity, NullPublicKey)

View File

@ -2,8 +2,8 @@ package net.corda.contracts.asset
import net.corda.contracts.clause.AbstractConserveAmount import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -40,7 +40,7 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
*/ */
@Throws(InsufficientBalanceException::class) @Throws(InsufficientBalanceException::class)
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>, fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>): CompositeKey { assetStates: List<StateAndRef<S>>): PublicKey {
return conserveClause.generateExit( return conserveClause.generateExit(
tx, tx,
amountIssued, amountIssued,
@ -60,5 +60,5 @@ abstract class OnLedgerAsset<T : Any, C : CommandData, S : FungibleAsset<T>> : C
* implementations to have fields in their state which we don't know about here, and we simply leave them untouched * implementations to have fields in their state which we don't know about here, and we simply leave them untouched
* when sending out "change" from spending/exiting. * when sending out "change" from spending/exiting.
*/ */
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: CompositeKey): TransactionState<S> abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKey): TransactionState<S>
} }

View File

@ -2,8 +2,8 @@ package net.corda.contracts.clause
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.crypto.CompositeKey
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import java.util.* import java.util.*
@ -59,9 +59,9 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
@Throws(InsufficientBalanceException::class) @Throws(InsufficientBalanceException::class)
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>, fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>, assetStates: List<StateAndRef<S>>,
deriveState: (TransactionState<S>, Amount<Issued<T>>, CompositeKey) -> TransactionState<S>, deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>,
generateMoveCommand: () -> CommandData, generateMoveCommand: () -> CommandData,
generateExitCommand: (Amount<Issued<T>>) -> CommandData): CompositeKey { generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey {
val owner = assetStates.map { it.state.data.owner }.toSet().singleOrNull() ?: throw InsufficientBalanceException(amountIssued) val owner = assetStates.map { it.state.data.owner }.toSet().singleOrNull() ?: throw InsufficientBalanceException(amountIssued)
val currency = amountIssued.token.product val currency = amountIssued.token.product
val amount = Amount(amountIssued.quantity, currency) val amount = Amount(amountIssued.quantity, currency)
@ -104,7 +104,7 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
val outputAmount: Amount<Issued<T>> = outputs.sumFungibleOrZero(groupingKey) val outputAmount: Amount<Issued<T>> = outputs.sumFungibleOrZero(groupingKey)
// If we want to remove assets from the ledger, that must be signed for by the issuer and owner. // If we want to remove assets from the ledger, that must be signed for by the issuer and owner.
val exitKeys: Set<CompositeKey> = inputs.flatMap { it.exitKeys }.toSet() val exitKeys: Set<PublicKey> = inputs.flatMap { it.exitKeys }.toSet()
val exitCommand = matchedCommands.select<FungibleAsset.Commands.Exit<T>>(parties = null, signers = exitKeys).filter { it.value.amount.token == groupingKey }.singleOrNull() val exitCommand = matchedCommands.select<FungibleAsset.Commands.Exit<T>>(parties = null, signers = exitKeys).filter { it.value.amount.token == groupingKey }.singleOrNull()
val amountExitingLedger: Amount<Issued<T>> = exitCommand?.value?.amount ?: Amount(0, groupingKey) val amountExitingLedger: Amount<Issued<T>> = exitCommand?.value?.amount ?: Amount(0, groupingKey)

View File

@ -6,7 +6,7 @@ import net.corda.contracts.asset.extractAmountsDue
import net.corda.contracts.asset.sumAmountsDue import net.corda.contracts.asset.sumAmountsDue
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.crypto.CompositeKey import java.security.PublicKey
/** /**
* Common interface for the state subsets used when determining nettability of two or more states. Exposes the * Common interface for the state subsets used when determining nettability of two or more states. Exposes the
@ -22,7 +22,7 @@ interface NetState<P : Any> {
* Bilateral states are used in close-out netting. * Bilateral states are used in close-out netting.
*/ */
data class BilateralNetState<P : Any>( data class BilateralNetState<P : Any>(
val partyKeys: Set<CompositeKey>, val partyKeys: Set<PublicKey>,
override val template: Obligation.Terms<P> override val template: Obligation.Terms<P>
) : NetState<P> ) : NetState<P>

View File

@ -4,10 +4,7 @@ import net.corda.core.contracts.Contract
import net.corda.core.contracts.DealState import net.corda.core.contracts.DealState
import net.corda.core.contracts.TransactionForContract import net.corda.core.contracts.TransactionForContract
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.crypto.AnonymousParty import net.corda.core.crypto.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey import java.security.PublicKey
@ -18,7 +15,7 @@ class DummyDealContract : Contract {
data class State( data class State(
override val contract: Contract = DummyDealContract(), override val contract: Contract = DummyDealContract(),
override val participants: List<CompositeKey> = listOf(), override val participants: List<PublicKey> = listOf(),
override val linearId: UniqueIdentifier = UniqueIdentifier(), override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val ref: String, override val ref: String,
override val parties: List<AnonymousParty> = listOf()) : DealState { override val parties: List<AnonymousParty> = listOf()) : DealState {

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.clauses.FilterOn
import net.corda.core.contracts.clauses.verifyClause import net.corda.core.contracts.clauses.verifyClause
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.containsAny
import java.security.PublicKey import java.security.PublicKey
class DummyLinearContract : Contract { class DummyLinearContract : Contract {
@ -19,7 +20,7 @@ class DummyLinearContract : Contract {
data class State( data class State(
override val linearId: UniqueIdentifier = UniqueIdentifier(), override val linearId: UniqueIdentifier = UniqueIdentifier(),
override val contract: Contract = DummyLinearContract(), override val contract: Contract = DummyLinearContract(),
override val participants: List<CompositeKey> = listOf(), override val participants: List<PublicKey> = listOf(),
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState { val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {

View File

@ -9,9 +9,7 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued import net.corda.core.contracts.Issued
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
@ -19,6 +17,7 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>) { fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>) {
@ -26,7 +25,7 @@ fun ServiceHub.fillWithSomeTestDeals(dealIds: List<String>) {
val transactions: List<SignedTransaction> = dealIds.map { val transactions: List<SignedTransaction> = dealIds.map {
// Issue a deal state // Issue a deal state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyDealContract.State(ref = it, participants = listOf(freshKey.public.composite))) addOutputState(DummyDealContract.State(ref = it, participants = listOf(freshKey.public)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
} }
@ -41,7 +40,7 @@ fun ServiceHub.fillWithSomeTestLinearStates(numberToCreate: Int) {
for (i in 1..numberToCreate) { for (i in 1..numberToCreate) {
// Issue a deal state // Issue a deal state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(DummyLinearContract.State(participants = listOf(freshKey.public.composite))) addOutputState(DummyLinearContract.State(participants = listOf(freshKey.public)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
} }
@ -65,12 +64,12 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
atMostThisManyStates: Int = 10, atMostThisManyStates: Int = 10,
rng: Random = Random(), rng: Random = Random(),
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })), ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
ownedBy: CompositeKey? = null, ownedBy: PublicKey? = null,
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER, issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault<Cash.State> { issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault<Cash.State> {
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng) val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
val myKey: CompositeKey = ownedBy ?: myInfo.legalIdentity.owningKey val myKey: PublicKey = ownedBy ?: myInfo.legalIdentity.owningKey
// We will allocate one state to one transaction, for simplicities sake. // We will allocate one state to one transaction, for simplicities sake.
val cash = Cash() val cash = Cash()

View File

@ -5,7 +5,7 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.InsufficientBalanceException import net.corda.core.contracts.InsufficientBalanceException
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.keys import net.corda.core.crypto.expandedCompositeKeys
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -44,7 +44,7 @@ open class CashPaymentFlow(
} }
progressTracker.currentStep = SIGNING_TX progressTracker.currentStep = SIGNING_TX
keysForSigning.keys.forEach { keysForSigning.expandedCompositeKeys.forEach {
val key = serviceHub.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}") val key = serviceHub.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}")
builder.signWith(KeyPair(it, key)) builder.signWith(KeyPair(it, key))
} }

View File

@ -16,6 +16,7 @@ import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
/** /**
@ -50,7 +51,7 @@ object TwoPartyTradeFlow {
data class SellerTradeInfo( data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>, val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>, val price: Amount<Currency>,
val sellerOwnerKey: CompositeKey val sellerOwnerKey: PublicKey
) )
@CordaSerializable @CordaSerializable
@ -95,7 +96,7 @@ object TwoPartyTradeFlow {
private fun receiveAndCheckProposedTransaction(): SignedTransaction { private fun receiveAndCheckProposedTransaction(): SignedTransaction {
progressTracker.currentStep = AWAITING_PROPOSAL progressTracker.currentStep = AWAITING_PROPOSAL
val myPublicKey = myKeyPair.public.composite val myPublicKey = myKeyPair.public
// Make the first message we'll send to kick off the flow. // Make the first message we'll send to kick off the flow.
val hello = SellerTradeInfo(assetToSell, price, myPublicKey) val hello = SellerTradeInfo(assetToSell, price, myPublicKey)
// What we get back from the other side is a transaction that *might* be valid and acceptable to us, // What we get back from the other side is a transaction that *might* be valid and acceptable to us,
@ -198,9 +199,9 @@ object TwoPartyTradeFlow {
} }
} }
private fun signWithOurKeys(cashSigningPubKeys: List<CompositeKey>, ptx: TransactionBuilder): SignedTransaction { private fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
// Now sign the transaction with whatever keys we need to move the cash. // Now sign the transaction with whatever keys we need to move the cash.
for (publicKey in cashSigningPubKeys.keys) { for (publicKey in cashSigningPubKeys.expandedCompositeKeys) {
val privateKey = serviceHub.keyManagementService.toPrivate(publicKey) val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
ptx.signWith(KeyPair(publicKey, privateKey)) ptx.signWith(KeyPair(publicKey, privateKey))
} }
@ -208,7 +209,7 @@ object TwoPartyTradeFlow {
return ptx.toSignedTransaction(checkSufficientSignatures = false) return ptx.toSignedTransaction(checkSufficientSignatures = false)
} }
private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<CompositeKey>> { private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKey>> {
val ptx = TransactionType.General.Builder(notary) val ptx = TransactionType.General.Builder(notary)
// Add input and output states for the movement of cash, by using the Cash contract to generate the states // Add input and output states for the movement of cash, by using the Cash contract to generate the states
@ -222,7 +223,7 @@ object TwoPartyTradeFlow {
// reveal who the owner actually is. The key management service is expected to derive a unique key from some // reveal who the owner actually is. The key management service is expected to derive a unique key from some
// initial seed in order to provide privacy protection. // initial seed in order to provide privacy protection.
val freshKey = serviceHub.keyManagementService.freshKey() val freshKey = serviceHub.keyManagementService.freshKey()
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public.composite) val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public)
tx.addOutputState(state, tradeRequest.assetForSale.state.notary) tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner) tx.addCommand(command, tradeRequest.assetForSale.state.data.owner)

View File

@ -4,7 +4,7 @@ import net.corda.contracts.asset.*
import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite import net.corda.core.crypto.SecureHash
import net.corda.core.days import net.corda.core.days
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
@ -271,8 +271,8 @@ class CommercialPaperTestsGeneric {
// Alice pays $9000 to BigCorp to own some of their debt. // Alice pays $9000 to BigCorp to own some of their debt.
moveTX = run { moveTX = run {
val ptx = TransactionType.General.Builder(DUMMY_NOTARY) val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public.composite) aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public)
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public.composite) CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public)
ptx.signWith(bigCorpServices.key) ptx.signWith(bigCorpServices.key)
ptx.signWith(aliceServices.key) ptx.signWith(aliceServices.key)
ptx.signWith(DUMMY_NOTARY_KEY) ptx.signWith(DUMMY_NOTARY_KEY)

View File

@ -24,6 +24,7 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.Closeable import java.io.Closeable
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
import kotlin.test.* import kotlin.test.*
@ -458,7 +459,7 @@ class CashTests {
// Spend tx generation // Spend tx generation
val OUR_KEY: KeyPair by lazy { generateKeyPair() } val OUR_KEY: KeyPair by lazy { generateKeyPair() }
val OUR_PUBKEY_1: CompositeKey get() = OUR_KEY.public.composite val OUR_PUBKEY_1: PublicKey get() = OUR_KEY.public
val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2 val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2
@ -484,7 +485,7 @@ class CashTests {
return tx.toWireTransaction() return tx.toWireTransaction()
} }
fun makeSpend(amount: Amount<Currency>, dest: CompositeKey): WireTransaction { fun makeSpend(amount: Amount<Currency>, dest: PublicKey): WireTransaction {
val tx = TransactionType.General.Builder(DUMMY_NOTARY) val tx = TransactionType.General.Builder(DUMMY_NOTARY)
databaseTransaction(database) { databaseTransaction(database) {
vault.generateSpend(tx, amount, dest) vault.generateSpend(tx, amount, dest)

View File

@ -2,13 +2,13 @@ package net.corda.contracts.asset
import net.corda.contracts.asset.Obligation.Lifecycle import net.corda.contracts.asset.Obligation.Lifecycle
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.NullPublicKey
import net.corda.core.crypto.NullCompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.utilities.* import net.corda.core.utilities.*
import net.corda.testing.* import net.corda.testing.*
import org.junit.Test import org.junit.Test
import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.* import java.util.*
@ -506,7 +506,7 @@ class ObligationTests {
val oneUnitFcoj = Amount(1, defaultFcoj) val oneUnitFcoj = Amount(1, defaultFcoj)
val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME) val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME)
val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE, val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE,
obligationDef, oneUnitFcoj.quantity, NullCompositeKey) obligationDef, oneUnitFcoj.quantity, NullPublicKey)
// Try settling a simple commodity obligation // Try settling a simple commodity obligation
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
@ -819,7 +819,7 @@ class ObligationTests {
val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement, val fiveKDollarsFromMegaToMini = Obligation.State(Lifecycle.NORMAL, MEGA_CORP, megaCorpDollarSettlement,
5000.DOLLARS.quantity, MINI_CORP_PUBKEY) 5000.DOLLARS.quantity, MINI_CORP_PUBKEY)
val amount = fiveKDollarsFromMegaToMini.amount val amount = fiveKDollarsFromMegaToMini.amount
val expected = mapOf(Pair(Pair(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Amount(amount.quantity, amount.token.product))) val expected: Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<Currency>>> = mapOf(Pair(Pair(MEGA_CORP_PUBKEY, MINI_CORP_PUBKEY), Amount(amount.quantity, amount.token.product)))
val actual = extractAmountsDue(megaCorpDollarSettlement, listOf(fiveKDollarsFromMegaToMini)) val actual = extractAmountsDue(megaCorpDollarSettlement, listOf(fiveKDollarsFromMegaToMini))
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -827,23 +827,23 @@ class ObligationTests {
@Test @Test
fun `netting equal balances due between parties`() { fun `netting equal balances due between parties`() {
// Now try it with two balances, which cancel each other out // Now try it with two balances, which cancel each other out
val balanced = mapOf( val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
) )
val expected: Map<Pair<CompositeKey, CompositeKey>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
val actual = netAmountsDue(balanced) val actual: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = netAmountsDue(balanced)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun `netting difference balances due between parties`() { fun `netting difference balances due between parties`() {
// Now try it with two balances, which cancel each other out // Now try it with two balances, which cancel each other out
val balanced = mapOf( val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(200000000, GBP)) Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(200000000, GBP))
) )
val expected = mapOf( val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
) )
val actual = netAmountsDue(balanced) val actual = netAmountsDue(balanced)
@ -852,16 +852,16 @@ class ObligationTests {
@Test @Test
fun `summing empty balances due between parties`() { fun `summing empty balances due between parties`() {
val empty = emptyMap<Pair<CompositeKey, CompositeKey>, Amount<Currency>>() val empty = emptyMap<Pair<PublicKey, PublicKey>, Amount<Currency>>()
val expected = emptyMap<CompositeKey, Long>() val expected = emptyMap<PublicKey, Long>()
val actual = sumAmountsDue(empty) val actual = sumAmountsDue(empty)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test @Test
fun `summing balances due between parties`() { fun `summing balances due between parties`() {
val simple = mapOf(Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP))) val simple: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)))
val expected = mapOf(Pair(ALICE_PUBKEY, -100000000L), Pair(BOB_PUBKEY, 100000000L)) val expected: Map<PublicKey, Long> = mapOf(Pair(ALICE_PUBKEY, -100000000L), Pair(BOB_PUBKEY, 100000000L))
val actual = sumAmountsDue(simple) val actual = sumAmountsDue(simple)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -869,11 +869,11 @@ class ObligationTests {
@Test @Test
fun `summing balances due between parties which net to zero`() { fun `summing balances due between parties which net to zero`() {
// Now try it with two balances, which cancel each other out // Now try it with two balances, which cancel each other out
val balanced = mapOf( val balanced: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = mapOf(
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
) )
val expected: Map<CompositeKey, Long> = emptyMap() // Zero balances are stripped before returning val expected: Map<PublicKey, Long> = emptyMap() // Zero balances are stripped before returning
val actual = sumAmountsDue(balanced) val actual = sumAmountsDue(balanced)
assertEquals(expected, actual) assertEquals(expected, actual)
} }

View File

@ -25,7 +25,7 @@ class ContractStateGenerator : Generator<ContractState>(ContractState::class.jav
override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState { override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState {
return Cash.State( return Cash.State(
amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status), amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status),
owner = CompositeKeyGenerator().generate(random, status) owner = PublicKeyGenerator().generate(random, status)
) )
} }
} }
@ -58,8 +58,8 @@ class CommandDataGenerator : Generator<CommandData>(CommandData::class.java) {
class CommandGenerator : Generator<Command>(Command::class.java) { class CommandGenerator : Generator<Command>(Command::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Command { override fun generate(random: SourceOfRandomness, status: GenerationStatus): Command {
val signersGenerator = ArrayListGenerator() val signersGenerator = ArrayListGenerator()
signersGenerator.addComponentGenerators(listOf(CompositeKeyGenerator())) signersGenerator.addComponentGenerators(listOf(PublicKeyGenerator()))
return Command(CommandDataGenerator().generate(random, status), CompositeKeyGenerator().generate(random, status)) return Command(CommandDataGenerator().generate(random, status), PublicKeyGenerator().generate(random, status))
} }
} }

View File

@ -2,7 +2,7 @@ package net.corda.nodeapi
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.toBase58String
import net.corda.core.messaging.MessageRecipientGroup import net.corda.core.messaging.MessageRecipientGroup
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
@ -11,6 +11,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.nodeapi.config.SSLConfiguration import net.corda.nodeapi.config.SSLConfiguration
import java.security.KeyStore import java.security.KeyStore
import java.security.PublicKey
/** /**
* The base class for Artemis services that defines shared data structures and SSL transport configuration. * The base class for Artemis services that defines shared data structures and SSL transport configuration.
@ -76,11 +77,11 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
@CordaSerializable @CordaSerializable
data class NodeAddress(override val queueName: String, override val hostAndPort: HostAndPort) : ArtemisPeerAddress { data class NodeAddress(override val queueName: String, override val hostAndPort: HostAndPort) : ArtemisPeerAddress {
companion object { companion object {
fun asPeer(peerIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress { fun asPeer(peerIdentity: PublicKey, hostAndPort: HostAndPort): NodeAddress {
return NodeAddress("$PEERS_PREFIX${peerIdentity.toBase58String()}", hostAndPort) return NodeAddress("$PEERS_PREFIX${peerIdentity.toBase58String()}", hostAndPort)
} }
fun asService(serviceIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress { fun asService(serviceIdentity: PublicKey, hostAndPort: HostAndPort): NodeAddress {
return NodeAddress("$SERVICES_PREFIX${serviceIdentity.toBase58String()}", hostAndPort) return NodeAddress("$SERVICES_PREFIX${serviceIdentity.toBase58String()}", hostAndPort)
} }
} }
@ -95,7 +96,7 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
* *
* @param identity The service identity's owning key. * @param identity The service identity's owning key.
*/ */
data class ServiceAddress(val identity: CompositeKey) : ArtemisAddress, MessageRecipientGroup { data class ServiceAddress(val identity: PublicKey) : ArtemisAddress, MessageRecipientGroup {
override val queueName: String = "$SERVICES_PREFIX${identity.toBase58String()}" override val queueName: String = "$SERVICES_PREFIX${identity.toBase58String()}"
} }

View File

@ -28,6 +28,7 @@ import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import rx.Observable import rx.Observable
import java.security.PublicKey
import sun.misc.MessageUtils.where import sun.misc.MessageUtils.where
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -80,12 +81,12 @@ class VaultSchemaTest {
private class VaultNoopContract : Contract { private class VaultNoopContract : Contract {
override val legalContractReference = SecureHash.sha256("") override val legalContractReference = SecureHash.sha256("")
data class VaultNoopState(override val owner: CompositeKey) : OwnableState { data class VaultNoopState(override val owner: PublicKey) : OwnableState {
override val contract = VaultNoopContract() override val contract = VaultNoopContract()
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: CompositeKey) = Pair(Commands.Create(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Create(), copy(owner = newOwner))
} }
interface Commands : CommandData { interface Commands : CommandData {
@ -114,7 +115,7 @@ class VaultSchemaTest {
val commands = emptyList<AuthenticatedObject<CommandData>>() val commands = emptyList<AuthenticatedObject<CommandData>>()
val attachments = emptyList<Attachment>() val attachments = emptyList<Attachment>()
val id = SecureHash.randomSHA256() val id = SecureHash.randomSHA256()
val signers = listOf(DUMMY_NOTARY_KEY.public.composite) val signers = listOf(DUMMY_NOTARY_KEY.public)
val timestamp: Timestamp? = null val timestamp: Timestamp? = null
transaction = LedgerTransaction( transaction = LedgerTransaction(
inputs, inputs,
@ -146,7 +147,7 @@ class VaultSchemaTest {
val commands = emptyList<AuthenticatedObject<CommandData>>() val commands = emptyList<AuthenticatedObject<CommandData>>()
val attachments = emptyList<Attachment>() val attachments = emptyList<Attachment>()
val id = SecureHash.randomSHA256() val id = SecureHash.randomSHA256()
val signers = listOf(DUMMY_NOTARY_KEY.public.composite) val signers = listOf(DUMMY_NOTARY_KEY.public)
val timestamp: Timestamp? = null val timestamp: Timestamp? = null
return LedgerTransaction( return LedgerTransaction(
inputs, inputs,
@ -634,7 +635,7 @@ class VaultSchemaTest {
@Test @Test
fun insertWithBigCompositeKey() { fun insertWithBigCompositeKey() {
val keys = (1..314).map { generateKeyPair().public.composite } val keys = (1..314).map { generateKeyPair().public }
val bigNotaryKey = CompositeKey.Builder().addKeys(keys).build() val bigNotaryKey = CompositeKey.Builder().addKeys(keys).build()
val vaultStEntity = VaultStatesEntity().apply { val vaultStEntity = VaultStatesEntity().apply {
txId = SecureHash.randomSHA256().toString() txId = SecureHash.randomSHA256().toString()

View File

@ -4,8 +4,8 @@ import co.paralleluniverse.fibers.Suspendable
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.client.rpc.CordaRPCClientImpl import net.corda.client.rpc.CordaRPCClientImpl
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.toBase58String
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
@ -90,7 +90,7 @@ abstract class MQSecurityTest : NodeBasedTest() {
@Test @Test
fun `create queue for unknown peer`() { fun `create queue for unknown peer`() {
val invalidPeerQueue = "$PEERS_PREFIX${generateKeyPair().public.composite.toBase58String()}" val invalidPeerQueue = "$PEERS_PREFIX${generateKeyPair().public.toBase58String()}"
assertAllQueueCreationAttacksFail(invalidPeerQueue) assertAllQueueCreationAttacksFail(invalidPeerQueue)
} }

View File

@ -4,7 +4,6 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UpgradedContract import net.corda.core.contracts.UpgradedContract
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
@ -27,6 +26,7 @@ import net.corda.node.utilities.databaseTransaction
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import rx.Observable import rx.Observable
import java.io.InputStream import java.io.InputStream
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -142,7 +142,7 @@ class CordaRPCOpsImpl(
} }
override fun waitUntilRegisteredWithNetworkMap() = services.networkMapCache.mapServiceRegistered override fun waitUntilRegisteredWithNetworkMap() = services.networkMapCache.mapServiceRegistered
override fun partyFromKey(key: CompositeKey) = services.identityService.partyFromKey(key) override fun partyFromKey(key: PublicKey) = services.identityService.partyFromKey(key)
override fun partyFromName(name: String) = services.identityService.partyFromName(name) override fun partyFromName(name: String) = services.identityService.partyFromName(name)
override fun registeredFlows(): List<String> = services.flowLogicRefFactory.flowWhitelist.keys.sorted() override fun registeredFlows(): List<String> = services.flowLogicRefFactory.flowWhitelist.keys.sorted()

View File

@ -2,12 +2,12 @@ package net.corda.node.services.identity
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.AnonymousParty 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 net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import java.security.PublicKey
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
@ -21,7 +21,7 @@ class InMemoryIdentityService : SingletonSerializeAsToken(), IdentityService {
private val log = loggerFor<InMemoryIdentityService>() private val log = loggerFor<InMemoryIdentityService>()
} }
private val keyToParties = ConcurrentHashMap<CompositeKey, Party>() private val keyToParties = ConcurrentHashMap<PublicKey, Party>()
private val nameToParties = ConcurrentHashMap<String, Party>() private val nameToParties = ConcurrentHashMap<String, Party>()
override fun registerIdentity(party: Party) { override fun registerIdentity(party: Party) {
@ -33,7 +33,7 @@ class InMemoryIdentityService : SingletonSerializeAsToken(), IdentityService {
// We give the caller a copy of the data set to avoid any locking problems // We give the caller a copy of the data set to avoid any locking problems
override fun getAllIdentities(): Iterable<Party> = ArrayList(keyToParties.values) override fun getAllIdentities(): Iterable<Party> = ArrayList(keyToParties.values)
override fun partyFromKey(key: CompositeKey): Party? = keyToParties[key] override fun partyFromKey(key: PublicKey): 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? = partyFromKey(party.owningKey) override fun partyFromAnonymous(party: AnonymousParty): Party? = partyFromKey(party.owningKey)
override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party) override fun partyFromAnonymous(partyRef: PartyAndReference) = partyFromAnonymous(partyRef.party)

View File

@ -279,7 +279,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
when { when {
queueName.startsWith(PEERS_PREFIX) -> try { queueName.startsWith(PEERS_PREFIX) -> try {
val identity = CompositeKey.parseFromBase58(queueName.substring(PEERS_PREFIX.length)) val identity = parsePublicKeyBase58(queueName.substring(PEERS_PREFIX.length))
val nodeInfo = networkMapCache.getNodeByLegalIdentityKey(identity) val nodeInfo = networkMapCache.getNodeByLegalIdentityKey(identity)
if (nodeInfo != null) { if (nodeInfo != null) {
deployBridgeToPeer(nodeInfo) deployBridgeToPeer(nodeInfo)
@ -291,7 +291,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
} }
queueName.startsWith(SERVICES_PREFIX) -> try { queueName.startsWith(SERVICES_PREFIX) -> try {
val identity = CompositeKey.parseFromBase58(queueName.substring(SERVICES_PREFIX.length)) val identity = parsePublicKeyBase58(queueName.substring(SERVICES_PREFIX.length))
val nodeInfos = networkMapCache.getNodesByAdvertisedServiceIdentityKey(identity) val nodeInfos = networkMapCache.getNodesByAdvertisedServiceIdentityKey(identity)
// Create a bridge for each node advertising the service. // Create a bridge for each node advertising the service.
for (nodeInfo in nodeInfos) { for (nodeInfo in nodeInfos) {

View File

@ -3,7 +3,6 @@ package net.corda.node.services.messaging
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.CompositeKey
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.NodeVersionInfo import net.corda.core.node.NodeVersionInfo
import net.corda.core.node.services.PartyInfo import net.corda.core.node.services.PartyInfo
@ -45,6 +44,7 @@ import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import java.security.PublicKey
// TODO: Stop the wallet explorer and other clients from using this class and get rid of persistentInbox // TODO: Stop the wallet explorer and other clients from using this class and get rid of persistentInbox
@ -70,7 +70,7 @@ import javax.annotation.concurrent.ThreadSafe
class NodeMessagingClient(override val config: NodeConfiguration, class NodeMessagingClient(override val config: NodeConfiguration,
nodeVersionInfo: NodeVersionInfo, nodeVersionInfo: NodeVersionInfo,
val serverHostPort: HostAndPort, val serverHostPort: HostAndPort,
val myIdentity: CompositeKey?, val myIdentity: PublicKey?,
val nodeExecutor: AffinityExecutor, val nodeExecutor: AffinityExecutor,
val database: Database, val database: Database,
val networkMapRegistrationFuture: ListenableFuture<Unit>, val networkMapRegistrationFuture: ListenableFuture<Unit>,

View File

@ -4,7 +4,6 @@ import com.google.common.annotations.VisibleForTesting
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import net.corda.core.bufferUntilSubscribed import net.corda.core.bufferUntilSubscribed
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.map import net.corda.core.map
import net.corda.core.messaging.MessagingService import net.corda.core.messaging.MessagingService
@ -28,6 +27,7 @@ import net.corda.node.utilities.bufferUntilDatabaseCommit
import net.corda.node.utilities.wrapWithDatabaseTransaction import net.corda.node.utilities.wrapWithDatabaseTransaction
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
@ -52,7 +52,7 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach
override val mapServiceRegistered: ListenableFuture<Unit> get() = _registrationFuture override val mapServiceRegistered: ListenableFuture<Unit> get() = _registrationFuture
private var registeredForPush = false private var registeredForPush = false
protected var registeredNodes: MutableMap<CompositeKey, NodeInfo> = Collections.synchronizedMap(HashMap()) protected var registeredNodes: MutableMap<PublicKey, NodeInfo> = Collections.synchronizedMap(HashMap())
override fun getPartyInfo(party: Party): PartyInfo? { override fun getPartyInfo(party: Party): PartyInfo? {
val node = registeredNodes[party.owningKey] val node = registeredNodes[party.owningKey]
@ -69,7 +69,7 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach
return null return null
} }
override fun getNodeByLegalIdentityKey(compositeKey: CompositeKey): NodeInfo? = registeredNodes[compositeKey] override fun getNodeByLegalIdentityKey(identityKey: PublicKey): NodeInfo? = registeredNodes[identityKey]
override fun track(): Pair<List<NodeInfo>, Observable<MapChange>> { override fun track(): Pair<List<NodeInfo>, Observable<MapChange>> {
synchronized(_changed) { synchronized(_changed) {

View File

@ -2,10 +2,7 @@ package net.corda.node.services.network
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.Party
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.signWithECDSA
import net.corda.core.messaging.MessageHandlerRegistration import net.corda.core.messaging.MessageHandlerRegistration
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
@ -311,7 +308,7 @@ data class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddO
*/ */
fun toWire(privateKey: PrivateKey): WireNodeRegistration { fun toWire(privateKey: PrivateKey): WireNodeRegistration {
val regSerialized = this.serialize() val regSerialized = this.serialize()
val regSig = privateKey.signWithECDSA(regSerialized.bytes, node.legalIdentity.owningKey.singleKey) val regSig = privateKey.signWithECDSA(regSerialized.bytes, node.legalIdentity.owningKey)
return WireNodeRegistration(regSerialized, regSig) return WireNodeRegistration(regSerialized, regSig)
} }

View File

@ -17,6 +17,8 @@ import net.corda.core.crypto.AbstractParty
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
import net.corda.core.crypto.containsAny
import net.corda.core.crypto.toBase58String
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.StatesNotAvailableException import net.corda.core.node.services.StatesNotAvailableException
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
@ -177,7 +179,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
override fun <T : ContractState> states(clazzes: Set<Class<T>>, statuses: EnumSet<Vault.StateStatus>, includeSoftLockedStates: Boolean): Iterable<StateAndRef<T>> { override fun <T : ContractState> states(clazzes: Set<Class<T>>, statuses: EnumSet<Vault.StateStatus>, includeSoftLockedStates: Boolean): Iterable<StateAndRef<T>> {
val stateAndRefs = val stateAndRefs =
session.withTransaction(TransactionIsolation.REPEATABLE_READ) { session.withTransaction(TransactionIsolation.REPEATABLE_READ) {
var query = select(VaultSchema.VaultStates::class) val query = select(VaultSchema.VaultStates::class)
.where(VaultSchema.VaultStates::stateStatus `in` statuses) .where(VaultSchema.VaultStates::stateStatus `in` statuses)
// TODO: temporary fix to continue supporting track() function (until becomes Typed) // TODO: temporary fix to continue supporting track() function (until becomes Typed)
if (!clazzes.map { it.name }.contains(ContractState::class.java.name)) if (!clazzes.map { it.name }.contains(ContractState::class.java.name))
@ -442,8 +444,8 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
@Suspendable @Suspendable
override fun generateSpend(tx: TransactionBuilder, override fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: CompositeKey, to: PublicKey,
onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<CompositeKey>> { onlyFromParties: Set<AbstractParty>?): Pair<TransactionBuilder, List<PublicKey>> {
// Discussion // Discussion
// //
// This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline. // This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline.
@ -520,7 +522,7 @@ class NodeVaultService(private val services: ServiceHub, dataSourceProperties: P
return Pair(tx, keysUsed) return Pair(tx, keysUsed)
} }
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: CompositeKey) private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))
/** /**

View File

@ -3,7 +3,6 @@ package net.corda.node.utilities
import co.paralleluniverse.strands.Strand import co.paralleluniverse.strands.Strand
import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource import com.zaxxer.hikari.HikariDataSource
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.parsePublicKeyBase58 import net.corda.core.crypto.parsePublicKeyBase58
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
@ -260,8 +259,7 @@ fun <T : Any> rx.Observable<T>.wrapWithDatabaseTransaction(db: Database? = null)
} }
// Composite columns for use with below Exposed helpers. // Composite columns for use with below Exposed helpers.
data class PartyColumns(val name: Column<String>, val owningKey: Column<CompositeKey>) data class PartyColumns(val name: Column<String>, val owningKey: Column<PublicKey>)
data class StateRefColumns(val txId: Column<SecureHash>, val index: Column<Int>) data class StateRefColumns(val txId: Column<SecureHash>, val index: Column<Int>)
data class TxnNoteColumns(val txId: Column<SecureHash>, val note: Column<String>) data class TxnNoteColumns(val txId: Column<SecureHash>, val note: Column<String>)
@ -269,10 +267,8 @@ data class TxnNoteColumns(val txId: Column<SecureHash>, val note: Column<String>
* [Table] column helpers for use with Exposed, as per [varchar] etc. * [Table] column helpers for use with Exposed, as per [varchar] etc.
*/ */
fun Table.publicKey(name: String) = this.registerColumn<PublicKey>(name, PublicKeyColumnType) fun Table.publicKey(name: String) = this.registerColumn<PublicKey>(name, PublicKeyColumnType)
fun Table.compositeKey(name: String) = this.registerColumn<CompositeKey>(name, CompositeKeyColumnType)
fun Table.secureHash(name: String) = this.registerColumn<SecureHash>(name, SecureHashColumnType) fun Table.secureHash(name: String) = this.registerColumn<SecureHash>(name, SecureHashColumnType)
fun Table.party(nameColumnName: String, keyColumnName: String) = PartyColumns(this.varchar(nameColumnName, length = 255), this.compositeKey(keyColumnName)) fun Table.party(nameColumnName: String, keyColumnName: String) = PartyColumns(this.varchar(nameColumnName, length = 255), this.publicKey(keyColumnName))
fun Table.uuidString(name: String) = this.registerColumn<UUID>(name, UUIDStringColumnType) fun Table.uuidString(name: String) = this.registerColumn<UUID>(name, UUIDStringColumnType)
fun Table.localDate(name: String) = this.registerColumn<LocalDate>(name, LocalDateColumnType) fun Table.localDate(name: String) = this.registerColumn<LocalDate>(name, LocalDateColumnType)
fun Table.localDateTime(name: String) = this.registerColumn<LocalDateTime>(name, LocalDateTimeColumnType) fun Table.localDateTime(name: String) = this.registerColumn<LocalDateTime>(name, LocalDateTimeColumnType)
@ -283,23 +279,17 @@ fun Table.txnNote(txIdColumnName: String, txnNoteColumnName: String) = TxnNoteCo
/** /**
* [ColumnType] for marshalling to/from database on behalf of [PublicKey]. * [ColumnType] for marshalling to/from database on behalf of [PublicKey].
*/ */
// TODO Rethink how we store CompositeKeys in db. Currently they are stored as Base58 strings and as we don't know the size
// of a CompositeKey they could be CLOB fields. Given the time to fetch these types and that they are unsuitable as table keys,
// having a shorter primary key (such as SHA256 hash or a UUID generated on demand) that references a common composite key table may make more sense.
object PublicKeyColumnType : ColumnType() { object PublicKeyColumnType : ColumnType() {
override fun sqlType(): String = "VARCHAR(255)" override fun sqlType(): String = "VARCHAR"
override fun valueFromDB(value: Any): Any = parsePublicKeyBase58(value.toString()) override fun valueFromDB(value: Any): Any = parsePublicKeyBase58(value.toString())
override fun notNullValueToDB(value: Any): Any = if (value is PublicKey) value.toBase58String() else value override fun notNullValueToDB(value: Any): Any = if (value is PublicKey) value.toBase58String() else value
} }
/**
* [ColumnType] for marshalling to/from database on behalf of [CompositeKey].
*/
object CompositeKeyColumnType : ColumnType() {
override fun sqlType(): String = "VARCHAR"
override fun valueFromDB(value: Any): Any = CompositeKey.parseFromBase58(value.toString())
override fun notNullValueToDB(value: Any): Any = if (value is CompositeKey) value.toBase58String() else value
}
/** /**
* [ColumnType] for marshalling to/from database on behalf of [SecureHash]. * [ColumnType] for marshalling to/from database on behalf of [SecureHash].
*/ */

View File

@ -2,7 +2,6 @@ package net.corda.node.utilities
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.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
@ -22,13 +21,13 @@ object ServiceIdentityGenerator {
* @param dirs List of node directories to place the generated identity and key pairs in. * @param dirs List of node directories to place the generated identity and key pairs in.
* @param serviceId The service id of the distributed service. * @param serviceId The service id of the distributed service.
* @param serviceName The legal name of the distributed service. * @param serviceName The legal name of the distributed service.
* @param threshold The threshold for the generated group [CompositeKey.Node]. * @param threshold The threshold for the generated group [CompositeKey].
*/ */
fun generateToDisk(dirs: List<Path>, serviceId: String, serviceName: String, threshold: Int = 1) { fun generateToDisk(dirs: List<Path>, serviceId: String, serviceName: String, threshold: Int = 1) {
log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" } log.trace { "Generating a group identity \"serviceName\" for nodes: ${dirs.joinToString()}" }
val keyPairs = (1..dirs.size).map { generateKeyPair() } val keyPairs = (1..dirs.size).map { generateKeyPair() }
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public.composite }).build(threshold) val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
val notaryParty = Party(serviceName, notaryKey).serialize() val notaryParty = Party(serviceName, notaryKey).serialize()
keyPairs.zip(dirs) { keyPair, dir -> keyPairs.zip(dirs) { keyPair, dir ->

View File

@ -2,6 +2,8 @@ package net.corda.node
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.crypto.keys
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.StateMachineUpdate import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow

View File

@ -40,6 +40,7 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.math.BigInteger import java.math.BigInteger
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
import java.util.concurrent.Future import java.util.concurrent.Future
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
@ -257,7 +258,7 @@ class TwoPartyTradeFlowTests {
} }
val extraKey = bobNode.keyManagement.freshKey() val extraKey = bobNode.keyManagement.freshKey()
val bobsFakeCash = fillUpForBuyer(false, extraKey.public.composite, val bobsFakeCash = fillUpForBuyer(false, extraKey.public,
DUMMY_CASH_ISSUER.party, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey) val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode, notaryNode, bobNode.services.legalIdentityKey, extraKey)
@ -357,7 +358,7 @@ class TwoPartyTradeFlowTests {
attachment(ByteArrayInputStream(stream.toByteArray())) attachment(ByteArrayInputStream(stream.toByteArray()))
} }
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public.composite, val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public,
DUMMY_CASH_ISSUER.party, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
insertFakeTransactions(bobsFakeCash, bobNode, notaryNode) insertFakeTransactions(bobsFakeCash, bobNode, notaryNode)
@ -458,7 +459,7 @@ class TwoPartyTradeFlowTests {
val bobKey = bobNode.services.legalIdentityKey val bobKey = bobNode.services.legalIdentityKey
val issuer = MEGA_CORP.ref(1, 2, 3) val issuer = MEGA_CORP.ref(1, 2, 3)
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public.composite, DUMMY_CASH_ISSUER.party, val bobsBadCash = fillUpForBuyer(bobError, bobKey.public, DUMMY_CASH_ISSUER.party,
notaryNode.info.notaryIdentity).second notaryNode.info.notaryIdentity).second
val alicesFakePaper = databaseTransaction(aliceNode.database) { val alicesFakePaper = databaseTransaction(aliceNode.database) {
fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey, fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey,
@ -505,7 +506,7 @@ class TwoPartyTradeFlowTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer( private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
withError: Boolean, withError: Boolean,
owner: CompositeKey, owner: PublicKey,
issuer: AnonymousParty, issuer: AnonymousParty,
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> { notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {
val interimOwnerKey = MEGA_CORP_PUBKEY val interimOwnerKey = MEGA_CORP_PUBKEY
@ -551,7 +552,7 @@ class TwoPartyTradeFlowTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller( private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
withError: Boolean, withError: Boolean,
owner: CompositeKey, owner: PublicKey,
amount: Amount<Issued<Currency>>, amount: Amount<Issued<Currency>>,
attachmentID: SecureHash?, attachmentID: SecureHash?,
notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> { notary: Party): Pair<Vault<ContractState>, List<WireTransaction>> {

View File

@ -1,7 +1,6 @@
package net.corda.node.services package net.corda.node.services
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.composite
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.node.services.identity.InMemoryIdentityService import net.corda.node.services.identity.InMemoryIdentityService
import net.corda.testing.ALICE import net.corda.testing.ALICE
@ -51,7 +50,7 @@ class InMemoryIdentityServiceTests {
@Test @Test
fun `get identity by name`() { fun `get identity by name`() {
val service = InMemoryIdentityService() val service = InMemoryIdentityService()
val identities = listOf("Node A", "Node B", "Node C").map { Party(it, generateKeyPair().public.composite) } val identities = listOf("Node A", "Node B", "Node C").map { Party(it, generateKeyPair().public) }
assertNull(service.partyFromName(identities.first().name)) assertNull(service.partyFromName(identities.first().name))
identities.forEach { service.registerIdentity(it) } identities.forEach { service.registerIdentity(it) }
identities.forEach { assertEquals(it, service.partyFromName(it.name)) } identities.forEach { assertEquals(it, service.partyFromName(it.name)) }

View File

@ -1,8 +1,6 @@
package net.corda.node.services package net.corda.node.services
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.composite
import net.corda.core.days import net.corda.core.days
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRef import net.corda.core.flows.FlowLogicRef
@ -113,7 +111,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
} }
class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState { class TestState(val flowLogicRef: FlowLogicRef, val instant: Instant) : LinearState, SchedulableState {
override val participants: List<CompositeKey> override val participants: List<PublicKey>
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
override val linearId = UniqueIdentifier() override val linearId = UniqueIdentifier()
@ -272,7 +270,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
val state = TestState(factory.create(TestFlowLogic::class.java, increment), instant) val state = TestState(factory.create(TestFlowLogic::class.java, increment), instant)
val usefulTX = TransactionType.General.Builder(null).apply { val usefulTX = TransactionType.General.Builder(null).apply {
addOutputState(state, DUMMY_NOTARY) addOutputState(state, DUMMY_NOTARY)
addCommand(Command(), freshKey.public.composite) addCommand(Command(), freshKey.public)
signWith(freshKey) signWith(freshKey)
}.toSignedTransaction() }.toSignedTransaction()
val txHash = usefulTX.id val txHash = usefulTX.id

View File

@ -6,6 +6,7 @@ import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.keys
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.seconds import net.corda.core.seconds

View File

@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.contracts.* import net.corda.core.contracts.*
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.containsAny
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
@ -44,7 +45,7 @@ class ScheduledFlowTests {
} }
} }
override val participants: List<CompositeKey> = listOf(source.owningKey, destination.owningKey) override val participants: List<PublicKey> = listOf(source.owningKey, destination.owningKey)
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { it.containsAny(ourKeys) } return participants.any { it.containsAny(ourKeys) }

View File

@ -3,7 +3,7 @@ package net.corda.node.services
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.composite import net.corda.core.crypto.keys
import net.corda.core.getOrThrow import net.corda.core.getOrThrow
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -54,7 +54,7 @@ class ValidatingNotaryServiceTests {
} }
@Test fun `should report error for missing signatures`() { @Test fun `should report error for missing signatures`() {
val expectedMissingKey = MEGA_CORP_KEY.public.composite val expectedMissingKey = MEGA_CORP_KEY.public
val stx = run { val stx = run {
val inputState = issueState(clientNode) val inputState = issueState(clientNode)

View File

@ -6,7 +6,6 @@ import net.corda.contracts.testing.fillWithSomeTestCash
import net.corda.contracts.testing.fillWithSomeTestDeals import net.corda.contracts.testing.fillWithSomeTestDeals
import net.corda.contracts.testing.fillWithSomeTestLinearStates import net.corda.contracts.testing.fillWithSomeTestLinearStates
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.composite
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.node.services.consumedStates import net.corda.core.node.services.consumedStates
import net.corda.core.node.services.unconsumedStates import net.corda.core.node.services.unconsumedStates
@ -82,7 +81,7 @@ class VaultWithCashTest {
val state = w[0].state.data val state = w[0].state.data
assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount) assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount)
assertEquals(services.key.public.composite, state.owner) assertEquals(services.key.public, state.owner)
assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[2].state.data).amount) assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[2].state.data).amount)
assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[1].state.data).amount) assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w[1].state.data).amount)
@ -95,7 +94,7 @@ class VaultWithCashTest {
// A tx that sends us money. // A tx that sends us money.
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
val usefulTX = TransactionType.General.Builder(null).apply { val usefulTX = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public.composite, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -113,7 +112,7 @@ class VaultWithCashTest {
// A tx that doesn't send us anything. // A tx that doesn't send us anything.
val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply { val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_KEY.public.composite, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_KEY.public, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -137,7 +136,7 @@ class VaultWithCashTest {
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L), services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 10, 10, Random(0L),
issuedBy = MEGA_CORP.ref(1), issuedBy = MEGA_CORP.ref(1),
issuerKey = MEGA_CORP_KEY, issuerKey = MEGA_CORP_KEY,
ownedBy = freshKey.public.composite) ownedBy = freshKey.public)
println("Cash balance: ${vault.cashBalances[USD]}") println("Cash balance: ${vault.cashBalances[USD]}")
assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10) assertThat(vault.unconsumedStates<Cash.State>()).hasSize(10)
@ -226,8 +225,8 @@ class VaultWithCashTest {
// Issue a linear state // Issue a linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.composite))) addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.composite))) addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -247,7 +246,7 @@ class VaultWithCashTest {
// Issue a linear state // Issue a linear state
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.composite))) addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -259,7 +258,7 @@ class VaultWithCashTest {
// Move the same state // Move the same state
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.composite))) addOutputState(net.corda.contracts.testing.DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
addInputState(dummyIssue.tx.outRef<LinearState>(0)) addInputState(dummyIssue.tx.outRef<LinearState>(0))
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -276,7 +275,7 @@ class VaultWithCashTest {
val freshKey = services.keyManagementService.freshKey() val freshKey = services.keyManagementService.freshKey()
databaseTransaction(database) { databaseTransaction(database) {
services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = freshKey.public.composite) services.fillWithSomeTestCash(100.DOLLARS, DUMMY_NOTARY, 3, 3, Random(0L), ownedBy = freshKey.public)
services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L)) services.fillWithSomeTestCash(100.SWISS_FRANCS, DUMMY_NOTARY, 2, 2, Random(0L))
services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L)) services.fillWithSomeTestCash(100.POUNDS, DUMMY_NOTARY, 1, 1, Random(0L))
val cash = vault.unconsumedStates<Cash.State>() val cash = vault.unconsumedStates<Cash.State>()
@ -320,8 +319,8 @@ class VaultWithCashTest {
// Create a txn consuming different contract types // Create a txn consuming different contract types
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply { val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
addOutputState(net.corda.contracts.testing.DummyLinearContract.State(participants = listOf(freshKey.public.composite))) addOutputState(net.corda.contracts.testing.DummyLinearContract.State(participants = listOf(freshKey.public)))
addOutputState(net.corda.contracts.testing.DummyDealContract.State(ref = "999", participants = listOf(freshKey.public.composite))) addOutputState(net.corda.contracts.testing.DummyDealContract.State(ref = "999", participants = listOf(freshKey.public)))
addInputState(linearStates.first()) addInputState(linearStates.first())
addInputState(deals.first()) addInputState(deals.first())
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)

Some files were not shown because too many files have changed in this diff Show More