Replace PublicKey with PublicKeyTree in Party. A single entity can now be identified by more than one key.

This commit is contained in:
Andrius Dagys 2016-11-11 18:14:50 +00:00
parent d7defd3157
commit c33c55eb20
84 changed files with 532 additions and 510 deletions

View File

@ -174,8 +174,9 @@ class NodeMonitorModelTest {
require(tx.tx.outputs.size == 1) require(tx.tx.outputs.size == 1)
val signaturePubKeys = tx.sigs.map { it.by }.toSet() val signaturePubKeys = tx.sigs.map { it.by }.toSet()
// Only Alice signed // Only Alice signed
require(signaturePubKeys.size == 1) val aliceKey = aliceNode.legalIdentity.owningKey
require(signaturePubKeys.contains(aliceNode.legalIdentity.owningKey)) require(signaturePubKeys.size <= aliceKey.keys.size)
require(aliceKey.isFulfilledBy(signaturePubKeys))
issueTx = tx issueTx = tx
}, },
// MOVE // MOVE
@ -184,9 +185,8 @@ class NodeMonitorModelTest {
require(tx.tx.outputs.size == 1) require(tx.tx.outputs.size == 1)
val signaturePubKeys = tx.sigs.map { it.by }.toSet() val signaturePubKeys = tx.sigs.map { it.by }.toSet()
// Alice and Notary signed // Alice and Notary signed
require(signaturePubKeys.size == 2) require(aliceNode.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys))
require(signaturePubKeys.contains(aliceNode.legalIdentity.owningKey)) require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys))
require(signaturePubKeys.contains(notaryNode.notaryIdentity.owningKey))
moveTx = tx moveTx = tx
} }
) )

View File

@ -1,13 +1,13 @@
package net.corda.client.model package net.corda.client.model
import javafx.collections.ObservableList
import kotlinx.support.jdk8.collections.removeIf
import net.corda.client.fxutils.foldToObservableList import net.corda.client.fxutils.foldToObservableList
import net.corda.client.fxutils.map import net.corda.client.fxutils.map
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import javafx.collections.ObservableList
import kotlinx.support.jdk8.collections.removeIf
import java.security.PublicKey
class NetworkIdentityModel { class NetworkIdentityModel {
private val networkIdentityObservable by observable(NodeMonitorModel::networkMap) private val networkIdentityObservable by observable(NodeMonitorModel::networkMap)
@ -34,7 +34,7 @@ class NetworkIdentityModel {
return advertisedServices.any { it.info.type == NetworkMapService.type || it.info.type.isNotary() } return advertisedServices.any { it.info.type == NetworkMapService.type || it.info.type.isNotary() }
} }
fun lookup(publicKey: PublicKey): NodeInfo? { fun lookup(publicKeyTree: PublicKeyTree): NodeInfo? {
return parties.firstOrNull { it.legalIdentity.owningKey == publicKey } ?: notaries.firstOrNull { it.notaryIdentity.owningKey == publicKey } return parties.firstOrNull { it.legalIdentity.owningKey == publicKeyTree } ?: notaries.firstOrNull { it.notaryIdentity.owningKey == publicKeyTree }
} }
} }

View File

@ -2,7 +2,7 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.security.PublicKey import net.corda.core.crypto.PublicKeyTree
import java.util.* import java.util.*
/** /**
@ -60,16 +60,16 @@ inline fun <R> requireThat(body: Requirements.() -> R) = R.body()
//// Authenticated commands /////////////////////////////////////////////////////////////////////////////////////////// //// Authenticated commands ///////////////////////////////////////////////////////////////////////////////////////////
/** 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: PublicKey? = null, inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signer: PublicKeyTree? = 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 }.
filter { if (party == null) true else party in it.signingParties }. filter { if (party == null) true else party in it.signingParties }.
map { AuthenticatedObject(it.signers, it.signingParties, it.value as T) } map { AuthenticatedObject(it.signers, it.signingParties, it.value as T) }
/** 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<PublicKey>?, inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signers: Collection<PublicKeyTree>?,
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)}.
filter { if (parties == null) true else it.signingParties.containsAll(parties) }. filter { if (parties == null) true else it.signingParties.containsAll(parties) }.

View File

@ -1,9 +1,9 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
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.
@ -15,12 +15,12 @@ class DummyContract : Contract {
val magicNumber: Int val magicNumber: Int
} }
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKey) : OwnableState, State { data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKeyTree) : OwnableState, State {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
} }
/** /**
@ -29,9 +29,9 @@ class DummyContract : Contract {
* 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<PublicKey>) : ContractState, State { val owners: List<PublicKeyTree>) : ContractState, State {
override val contract = DUMMY_PROGRAM_ID override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = owners get() = owners
} }
@ -54,8 +54,8 @@ class DummyContract : Contract {
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owner.party.owningKey)) return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owner.party.owningKey))
} }
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner) fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKeyTree) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder { fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKeyTree): TransactionBuilder {
require(priors.size > 0) require(priors.size > 0)
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,12 +1,11 @@
package net.corda.core.contracts package net.corda.core.contracts
import java.security.PublicKey import net.corda.core.crypto.PublicKeyTree
/** /**
* 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<PublicKey> override val participants: List<PublicKeyTree>
get() = emptyList() get() = emptyList()
} }

View File

@ -1,6 +1,6 @@
package net.corda.core.contracts package net.corda.core.contracts
import java.security.PublicKey import net.corda.core.crypto.PublicKeyTree
class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() { class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() {
override fun toString() = "Insufficient balance, missing $amountMissing" override fun toString() = "Insufficient balance, missing $amountMissing"
@ -26,10 +26,11 @@ interface FungibleAsset<T> : 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<PublicKey> val exitKeys: Collection<PublicKeyTree>
/** 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: PublicKey override val owner: PublicKeyTree
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKey): FungibleAsset<T>
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKeyTree): FungibleAsset<T>
// Just for grouping // Just for grouping
interface Commands : CommandData { interface Commands : CommandData {

View File

@ -2,8 +2,8 @@ package net.corda.core.contracts
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.protocols.ProtocolLogicRef import net.corda.core.protocols.ProtocolLogicRef
import net.corda.core.protocols.ProtocolLogicRefFactory import net.corda.core.protocols.ProtocolLogicRefFactory
@ -113,7 +113,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<PublicKey> val participants: List<PublicKeyTree>
/** /**
* All contract states may be _encumbered_ by up to one other state. * All contract states may be _encumbered_ by up to one other state.
@ -184,10 +184,10 @@ fun <T> Amount<Issued<T>>.withoutIssuer(): Amount<T> = Amount(quantity, token.pr
*/ */
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: PublicKey val owner: PublicKeyTree
/** 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: PublicKey): Pair<CommandData, OwnableState> fun withNewOwner(newOwner: PublicKeyTree): 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 */
@ -351,15 +351,15 @@ 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 */
data class Command(val value: CommandData, val signers: List<PublicKey>) { data class Command(val value: CommandData, val signers: List<PublicKeyTree>) {
init { init {
require(signers.isNotEmpty()) require(signers.isNotEmpty())
} }
constructor(data: CommandData, key: PublicKey) : this(data, listOf(key)) constructor(data: CommandData, key: PublicKeyTree) : 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.map { it.toStringShort() }}" override fun toString() = "${commandDataToString()} with pubkeys ${signers.joinToString()}"
} }
/** A common issue command, to enforce that issue commands have a nonce value. */ /** A common issue command, to enforce that issue commands have a nonce value. */
@ -386,7 +386,7 @@ interface NetCommand : CommandData {
/** 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. */
data class AuthenticatedObject<out T : Any>( data class AuthenticatedObject<out T : Any>(
val signers: List<PublicKey>, val signers: List<PublicKeyTree>,
/** 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,9 +1,9 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
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 */
sealed class TransactionType { sealed class TransactionType {
@ -25,7 +25,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<PublicKey> { fun verifySigners(tx: LedgerTransaction): Set<PublicKeyTree> {
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)
@ -39,7 +39,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<PublicKey> abstract fun getRequiredSigners(tx: LedgerTransaction): Set<PublicKeyTree>
/** 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,10 +1,9 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
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.
@ -94,8 +93,8 @@ class TransactionConflictException(val conflictRef: StateRef, val tx1: LedgerTra
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : Exception(cause) { sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : Exception(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<PublicKey>) : TransactionVerificationException(tx, null) { class SignersMissing(tx: LedgerTransaction, val missing: List<PublicKeyTree>) : TransactionVerificationException(tx, null) {
override fun toString() = "Signers missing: ${missing.map { it.toStringShort() }}" override fun toString() = "Signers missing: ${missing.joinToString()}"
} }
class InvalidNotaryChange(tx: LedgerTransaction) : TransactionVerificationException(tx, null) class InvalidNotaryChange(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) { class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) {

View File

@ -31,7 +31,8 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
fun verifyWithECDSA(content: OpaqueBytes) = by.verifyWithECDSA(content.bits, this) fun verifyWithECDSA(content: OpaqueBytes) = by.verifyWithECDSA(content.bits, this)
} }
class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey, bits) // 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)
} }
object NullPublicKey : PublicKey, Comparable<PublicKey> { object NullPublicKey : PublicKey, Comparable<PublicKey> {
@ -42,6 +43,8 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun toString() = "NULL_KEY" override fun toString() = "NULL_KEY"
} }
val NullPublicKeyTree = NullPublicKey.tree
// TODO: Clean up this duplication between Null and Dummy public key // TODO: Clean up this duplication between Null and Dummy public key
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> { class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "DUMMY" override fun getAlgorithm() = "DUMMY"
@ -78,7 +81,7 @@ fun KeyPair.signWithECDSA(bitsToSign: ByteArray) = private.signWithECDSA(bitsToS
fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes) = private.signWithECDSA(bitsToSign.bits, public) fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes) = private.signWithECDSA(bitsToSign.bits, public)
fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes, party: Party) = signWithECDSA(bitsToSign.bits, party) fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes, party: Party) = signWithECDSA(bitsToSign.bits, party)
fun KeyPair.signWithECDSA(bitsToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable { fun KeyPair.signWithECDSA(bitsToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
check(public == party.owningKey) check(public in party.owningKey.keys)
val sig = signWithECDSA(bitsToSign) val sig = signWithECDSA(bitsToSign)
return DigitalSignature.LegallyIdentifiable(party, sig.bits) return DigitalSignature.LegallyIdentifiable(party, sig.bits)
} }
@ -99,8 +102,6 @@ fun PublicKey.toStringShort(): String {
} ?: toString() } ?: toString()
} }
fun Iterable<PublicKey>.toStringsShort(): String = map { it.toStringShort() }.toString()
/** Creates a [PublicKeyTree] with a single leaf node containing the public key */ /** Creates a [PublicKeyTree] with a single leaf node containing the public key */
val PublicKey.tree: PublicKeyTree get() = PublicKeyTree.Leaf(this) val PublicKey.tree: PublicKeyTree get() = PublicKeyTree.Leaf(this)

View File

@ -4,10 +4,28 @@ import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import java.security.PublicKey import java.security.PublicKey
/** A [Party] is well known (name, pubkey) pair. In a real system this would probably be an X.509 certificate. */ /**
data class Party(val name: String, val owningKey: PublicKey) { * 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
* keys, the "public key" of a party is represented by a composite construct a [PublicKeyTree], which combines multiple
* 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.
* Her advertised [Party] then has a legal [name] "Alice" and an [owingKey] "pub1 or pub2".
*
* [Party] is also used for service identities. E.g. Alice may also be running an interest rate oracle on her Corda node,
* which requires a separate signing key (and an identifying name). Services can also be distributed run by a coordinated
* cluster of Corda nodes. A [Party] representing a distributed service will have a public key tree composed of the
* individual cluster nodes' public keys. Each of the nodes in the cluster will advertise the same group [Party].
*
* @see PublicKeyTree
*/
data class Party(val name: String, val owningKey: PublicKeyTree) {
/** A helper constructor that converts the given [PublicKey] in to a [PublicKeyTree] with a single node */
constructor(name: String, owningKey: PublicKey) : this(name, owningKey.tree)
override fun toString() = name override fun toString() = name
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes) fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes)
fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes)) fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes))
} }

View File

@ -26,10 +26,10 @@ sealed class PublicKeyTree {
fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key)) fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key))
/** Returns all [PublicKey]s contained within the tree leaves */ /** Returns all [PublicKey]s contained within the tree leaves */
abstract fun getKeys(): Set<PublicKey> abstract val keys: Set<PublicKey>
/** Checks whether any of the given [keys] matches a leaf on the tree */ /** Checks whether any of the given [keys] matches a leaf on the tree */
fun containsAny(keys: Iterable<PublicKey>) = getKeys().intersect(keys).isNotEmpty() fun containsAny(otherKeys: Iterable<PublicKey>) = keys.intersect(otherKeys).isNotEmpty()
// TODO: implement a proper encoding/decoding mechanism // TODO: implement a proper encoding/decoding mechanism
fun toBase58String(): String = Base58.encode(this.serialize().bits) fun toBase58String(): String = Base58.encode(this.serialize().bits)
@ -42,21 +42,17 @@ sealed class PublicKeyTree {
class Leaf(val publicKey: PublicKey) : PublicKeyTree() { class Leaf(val publicKey: PublicKey) : PublicKeyTree() {
override fun isFulfilledBy(keys: Iterable<PublicKey>) = publicKey in keys override fun isFulfilledBy(keys: Iterable<PublicKey>) = publicKey in keys
override fun getKeys(): Set<PublicKey> = setOf(publicKey) override val keys: Set<PublicKey>
get() = setOf(publicKey)
// Auto-generated. TODO: remove once data class inheritance is enabled // TODO: remove once data class inheritance is enabled
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true return this === other || other is Leaf && other.publicKey == this.publicKey
if (other?.javaClass != javaClass) return false
other as Leaf
if (publicKey != other.publicKey) return false
return true
} }
override fun hashCode() = publicKey.hashCode() override fun hashCode() = publicKey.hashCode()
override fun toString() = publicKey.toStringShort()
} }
/** /**
@ -78,7 +74,8 @@ sealed class PublicKeyTree {
return totalWeight >= threshold return totalWeight >= threshold
} }
override fun getKeys(): Set<PublicKey> = children.flatMap { it.getKeys() }.toSet() override val keys: Set<PublicKey>
get() = children.flatMap { it.keys }.toSet()
// Auto-generated. TODO: remove once data class inheritance is enabled // Auto-generated. TODO: remove once data class inheritance is enabled
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -100,6 +97,8 @@ sealed class PublicKeyTree {
result = 31 * result + children.hashCode() result = 31 * result + children.hashCode()
return result return result
} }
override fun toString() = "(${children.joinToString()})"
} }
/** A helper class for building a [PublicKeyTree.Node]. */ /** A helper class for building a [PublicKeyTree.Node]. */
@ -119,8 +118,7 @@ sealed class PublicKeyTree {
return this return this
} }
fun addLeaves(publicKeys: List<PublicKey>): Builder = addLeaves(*publicKeys.toTypedArray()) fun addKeys(publicKeys: List<PublicKeyTree>): Builder = addKeys(*publicKeys.toTypedArray())
fun addLeaves(vararg publicKeys: PublicKey) = addKeys(*publicKeys.map { it.tree }.toTypedArray())
/** /**
* Builds the [PublicKeyTree.Node]. If [threshold] is not specified, it will default to * Builds the [PublicKeyTree.Node]. If [threshold] is not specified, it will default to
@ -131,7 +129,16 @@ sealed class PublicKeyTree {
else Node(threshold ?: children.size, children.toList(), weights.toList()) else Node(threshold ?: children.size, children.toList(), weights.toList())
} }
} }
/**
* Returns the enclosed [PublicKey] for a [PublicKeyTree] with a single node
*
* @throws IllegalArgumentException if the [PublicKeyTree] contains more than one node
*/
val singleKey: PublicKey
get() = keys.singleOrNull() ?: throw IllegalStateException("The public key tree has more than one node")
} }
/** Returns the set of all [PublicKey]s contained in the leaves of the [PublicKeyTree]s */ /** Returns the set of all [PublicKey]s contained in the leaves of the [PublicKeyTree]s */
fun Iterable<PublicKeyTree>.getKeys() = flatMap { it.getKeys() }.toSet() val Iterable<PublicKeyTree>.keys: Set<PublicKey>
get() = flatMap { it.keys }.toSet()

View File

@ -1,13 +1,13 @@
package net.corda.core.node package net.corda.core.node
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.transactions.SignedTransaction
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionResolutionException import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.contracts.TransactionState import net.corda.core.contracts.TransactionState
import net.corda.core.messaging.MessagingService import net.corda.core.messaging.MessagingService
import net.corda.core.node.services.* import net.corda.core.node.services.*
import net.corda.core.protocols.ProtocolLogic import net.corda.core.protocols.ProtocolLogic
import net.corda.core.transactions.SignedTransaction
import java.security.KeyPair import java.security.KeyPair
import java.time.Clock import java.time.Clock
@ -59,8 +59,11 @@ interface ServiceHub {
* Helper property to shorten code for fetching the Node's KeyPair associated with the * Helper property to shorten code for fetching the Node's KeyPair associated with the
* public legalIdentity Party from the key management service. * public legalIdentity Party from the key management service.
* Typical use is during signing in protocols and for unit test signing. * Typical use is during signing in protocols and for unit test signing.
*
* TODO: legalIdentity can now be composed of multiple keys, should we return a list of keyPairs here? Right now
* the logic assumes the legal identity has a public key tree with only one node
*/ */
val legalIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.legalIdentity.owningKey) val legalIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.legalIdentity.owningKey.keys)
/** /**
* Helper property to shorten code for fetching the Node's KeyPair associated with the * Helper property to shorten code for fetching the Node's KeyPair associated with the
@ -69,7 +72,7 @@ interface ServiceHub {
* an IllegalArgumentException. * an IllegalArgumentException.
* Typical use is during signing in protocols and for unit test signing. * Typical use is during signing in protocols and for unit test signing.
*/ */
val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey) val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey.keys)
} }
/** /**

View File

@ -1,7 +1,7 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.security.PublicKey import net.corda.core.crypto.PublicKeyTree
/** /**
* 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
@ -15,6 +15,6 @@ 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: PublicKey): Party? fun partyFromKey(key: PublicKeyTree): Party?
fun partyFromName(name: String): Party? fun partyFromName(name: String): Party?
} }

View File

@ -4,12 +4,12 @@ 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.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.messaging.MessagingService import net.corda.core.messaging.MessagingService
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
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,7 +74,7 @@ interface NetworkMapCache {
/** /**
* Look up the node info for a public key. * Look up the node info for a public key.
*/ */
fun getNodeByPublicKey(publicKey: PublicKey): NodeInfo? fun getNodeByPublicKey(publicKey: PublicKeyTree): NodeInfo?
/** /**
* Add a network map service; fetches a copy of the latest map from the service and subscribes to any further * Add a network map service; fetches a copy of the latest map from the service and subscribes to any further

View File

@ -4,7 +4,9 @@ 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.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import rx.Observable import rx.Observable
@ -184,8 +186,8 @@ interface VaultService {
@Throws(InsufficientBalanceException::class) @Throws(InsufficientBalanceException::class)
fun generateSpend(tx: TransactionBuilder, fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: PublicKey, to: PublicKeyTree,
onlyFromParties: Set<Party>? = null): Pair<TransactionBuilder, List<PublicKey>> onlyFromParties: Set<Party>? = null): Pair<TransactionBuilder, List<PublicKeyTree>>
} }
inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java) inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java)
@ -201,14 +203,19 @@ inline fun <reified T : DealState> VaultService.dealsWith(party: Party) = linear
* The current interface is obviously not usable for those use cases: this is just where we'd put a real signing * The current interface is obviously not usable for those use cases: this is just where we'd put a real signing
* interface if/when one is developed. * interface if/when one is developed.
*/ */
interface KeyManagementService { 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>
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key") // TODO: make toPrivate return null if not found instead of throwing
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)) fun toKeyPair(publicKey: PublicKey) = KeyPair(publicKey, toPrivate(publicKey))
/** Returns the first [KeyPair] matching any of the [publicKeys] */
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. */
fun freshKey(): KeyPair fun freshKey(): KeyPair
} }

View File

@ -10,6 +10,8 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.serializers.JavaSerializer import com.esotericsoftware.kryo.serializers.JavaSerializer
import com.esotericsoftware.kryo.serializers.MapSerializer import com.esotericsoftware.kryo.serializers.MapSerializer
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.node.AttachmentsClassLoader import net.corda.core.node.AttachmentsClassLoader
@ -18,9 +20,6 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.NonEmptySet import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.NonEmptySetSerializer import net.corda.core.utilities.NonEmptySetSerializer
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.core.crypto.*
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
@ -32,7 +31,6 @@ import java.io.ObjectOutputStream
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
@ -268,7 +266,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<PublicKey> val signers = kryo.readClassAndObject(input) as List<PublicKeyTree>
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?

View File

@ -6,9 +6,7 @@ import com.pholser.junit.quickcheck.generator.java.lang.StringGenerator
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
import com.pholser.junit.quickcheck.random.SourceOfRandomness import com.pholser.junit.quickcheck.random.SourceOfRandomness
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.Party import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
@ -41,9 +39,15 @@ class PublicKeyGenerator: Generator<PublicKey>(PublicKey::class.java) {
} }
} }
class PublicKeyTreeGenerator : Generator<PublicKeyTree>(PublicKeyTree::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PublicKeyTree {
return entropyToKeyPair(random.nextBigInteger(32)).public.tree
}
}
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), PublicKeyGenerator().generate(random, status)) return Party(StringGenerator().generate(random, status), PublicKeyTreeGenerator().generate(random, status))
} }
} }

View File

@ -2,7 +2,7 @@ package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import java.security.PublicKey import net.corda.core.crypto.PublicKeyTree
import java.util.* import java.util.*
/** /**
@ -25,7 +25,7 @@ abstract class BaseTransaction(
* transaction until the transaction is verified by using [LedgerTransaction.verify]. It includes the * transaction until the transaction is verified by using [LedgerTransaction.verify]. It includes the
* notary key, if the notary field is set. * notary key, if the notary field is set.
*/ */
val mustSign: List<PublicKey>, val mustSign: List<PublicKeyTree>,
/** /**
* 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

@ -2,8 +2,8 @@ package net.corda.core.transactions
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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
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:
@ -27,7 +27,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<PublicKey>, signers: List<PublicKeyTree>,
timestamp: Timestamp?, timestamp: Timestamp?,
type: TransactionType type: TransactionType
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) { ) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {

View File

@ -3,12 +3,11 @@ package net.corda.core.transactions
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.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringsShort
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.util.* import java.util.*
@ -34,9 +33,9 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
/** Lazily calculated access to the deserialised/hashed transaction data. */ /** Lazily calculated access to the deserialised/hashed transaction data. */
val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) } val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) }
class SignaturesMissingException(val missing: Set<PublicKey>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() { class SignaturesMissingException(val missing: Set<PublicKeyTree>, 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.toStringsShort()}" return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}"
} }
} }
@ -53,7 +52,7 @@ 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: PublicKey): WireTransaction { fun verifySignatures(vararg allowedToBeMissing: PublicKeyTree): 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()
@ -83,19 +82,17 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
} }
} }
private fun getMissingSignatures(): Set<PublicKey> { private fun getMissingSignatures(): Set<PublicKeyTree> {
val requiredKeys = tx.mustSign.toSet()
val sigKeys = sigs.map { it.by }.toSet() val sigKeys = sigs.map { it.by }.toSet()
val missing = tx.mustSign.filter { !it.isFulfilledBy(sigKeys) }.toSet()
if (sigKeys.containsAll(requiredKeys)) return emptySet() return missing
return requiredKeys - sigKeys
} }
/** /**
* 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<PublicKey>): ArrayList<String> { private fun getMissingKeyDescriptions(missing: Set<PublicKeyTree>): 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

@ -3,11 +3,7 @@ package net.corda.core.transactions
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.crypto.*
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.*
@ -35,7 +31,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<PublicKey> = mutableSetOf(), protected val signers: MutableSet<PublicKeyTree> = mutableSetOf(),
protected var timestamp: Timestamp? = null) { protected var timestamp: Timestamp? = null) {
val time: Timestamp? get() = timestamp val time: Timestamp? get() = timestamp
@ -124,7 +120,7 @@ open class TransactionBuilder(
* @throws IllegalArgumentException if the signature key doesn't appear in any command. * @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/ */
fun checkSignature(sig: DigitalSignature.WithKey) { fun checkSignature(sig: DigitalSignature.WithKey) {
require(commands.any { it.signers.contains(sig.by) }) { "Signature key doesn't match any command" } require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
sig.verifyWithECDSA(toWireTransaction().id) sig.verifyWithECDSA(toWireTransaction().id)
} }
@ -140,9 +136,9 @@ 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<PublicKey> = signers - gotKeys val missing: Set<PublicKeyTree> = signers.filter { !it.isFulfilledBy(gotKeys) }.toSet()
if (missing.isNotEmpty()) if (missing.isNotEmpty())
throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.toStringsShort()}") throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.joinToString()}")
} }
val wtx = toWireTransaction() val wtx = toWireTransaction()
return SignedTransaction(wtx.serialize(), ArrayList(currentSigs), wtx.id) return SignedTransaction(wtx.serialize(), ArrayList(currentSigs), wtx.id)
@ -182,8 +178,8 @@ open class TransactionBuilder(
commands.add(arg) commands.add(arg)
} }
fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys))) fun addCommand(data: CommandData, vararg keys: PublicKeyTree) = addCommand(Command(data, listOf(*keys)))
fun addCommand(data: CommandData, keys: List<PublicKey>) = addCommand(Command(data, keys)) fun addCommand(data: CommandData, keys: List<PublicKeyTree>) = 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

@ -3,6 +3,7 @@ package net.corda.core.transactions
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.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.indexOfOrThrow import net.corda.core.indexOfOrThrow
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
@ -29,7 +30,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. */
val commands: List<Command>, val commands: List<Command>,
notary: Party?, notary: Party?,
signers: List<PublicKey>, signers: List<PublicKeyTree>,
type: TransactionType, type: TransactionType,
timestamp: Timestamp? timestamp: Timestamp?
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) { ) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {

View File

@ -1,7 +1,7 @@
package net.corda.core.utilities package net.corda.core.utilities
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.parsePublicKeyBase58 import net.corda.core.crypto.PublicKeyTree
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
@ -17,7 +17,7 @@ class ApiUtils(val services: ServiceHub) {
*/ */
fun withParty(partyKeyStr: String, notFound: (String) -> Response = defaultNotFound, found: (Party) -> Response): Response { fun withParty(partyKeyStr: String, notFound: (String) -> Response = defaultNotFound, found: (Party) -> Response): Response {
return try { return try {
val partyKey = parsePublicKeyBase58(partyKeyStr) val partyKey = PublicKeyTree.parseFromBase58(partyKeyStr)
val party = services.identityService.partyFromKey(partyKey) val party = services.identityService.partyFromKey(partyKey)
if(party == null) notFound("Unknown party") else found(party) if(party == null) notFound("Unknown party") else found(party)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {

View File

@ -2,20 +2,15 @@
package net.corda.core.utilities package net.corda.core.utilities
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.crypto.DummyPublicKey
import net.corda.core.crypto.Party
import net.corda.core.crypto.entropyToKeyPair
import net.corda.core.crypto.generateKeyPair
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: PublicKey get() = DummyPublicKey("x1") val DUMMY_PUBKEY_1: PublicKeyTree get() = DummyPublicKey("x1").tree
val DUMMY_PUBKEY_2: PublicKey get() = DummyPublicKey("x2") val DUMMY_PUBKEY_2: PublicKeyTree get() = DummyPublicKey("x2").tree
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

@ -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.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.signWithECDSA import net.corda.core.crypto.signWithECDSA
import net.corda.core.node.recordTransactions import net.corda.core.node.recordTransactions
import net.corda.core.protocols.ProtocolLogic import net.corda.core.protocols.ProtocolLogic
@ -15,7 +16,6 @@ import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
import net.corda.protocols.AbstractStateReplacementProtocol.Acceptor import net.corda.protocols.AbstractStateReplacementProtocol.Acceptor
import net.corda.protocols.AbstractStateReplacementProtocol.Instigator import net.corda.protocols.AbstractStateReplacementProtocol.Instigator
import java.security.PublicKey
/** /**
* Abstract protocol to be used for replacing one state with another, for example when changing the notary of a state. * Abstract protocol to be used for replacing one state with another, for example when changing the notary of a state.
@ -67,10 +67,10 @@ abstract class AbstractStateReplacementProtocol<T> {
} }
abstract protected fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal<T> abstract protected fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal<T>
abstract protected fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> abstract protected fun assembleTx(): Pair<SignedTransaction, List<PublicKeyTree>>
@Suspendable @Suspendable
private fun collectSignatures(participants: List<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> { private fun collectSignatures(participants: List<PublicKeyTree>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
val parties = participants.map { val parties = participants.map {
val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(it) ?: val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(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")
@ -95,7 +95,7 @@ abstract class AbstractStateReplacementProtocol<T> {
val participantSignature = response.unwrap { val participantSignature = response.unwrap {
if (it.sig == null) throw StateReplacementException(it.error!!) if (it.sig == null) throw StateReplacementException(it.error!!)
else { else {
check(it.sig.by == party.owningKey) { "Not signed by the required participant" } check(party.owningKey.isFulfilledBy(it.sig.by)) { "Not signed by the required participant" }
it.sig.verifyWithECDSA(stx.id) it.sig.verifyWithECDSA(stx.id)
it.sig it.sig
} }

View File

@ -50,5 +50,9 @@ class FinalityProtocol(val transaction: SignedTransaction,
} }
private fun needsNotarySignature(stx: SignedTransaction) = stx.tx.notary != null && hasNoNotarySignature(stx) private fun needsNotarySignature(stx: SignedTransaction) = stx.tx.notary != null && hasNoNotarySignature(stx)
private fun hasNoNotarySignature(stx: SignedTransaction) = stx.tx.notary?.owningKey !in stx.sigs.map { it.by } private fun hasNoNotarySignature(stx: SignedTransaction): Boolean {
val notaryKey = stx.tx.notary?.owningKey
val signers = stx.sigs.map { it.by }.toSet()
return !(notaryKey?.isFulfilledBy(signers) ?: false)
}
} }

View File

@ -6,12 +6,12 @@ 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.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
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.protocols.NotaryChangeProtocol.Acceptor import net.corda.protocols.NotaryChangeProtocol.Acceptor
import net.corda.protocols.NotaryChangeProtocol.Instigator import net.corda.protocols.NotaryChangeProtocol.Instigator
import java.security.PublicKey
/** /**
* A protocol to be used for changing a state's Notary. This is required since all input states to a transaction * A protocol to be used for changing a state's Notary. This is required since all input states to a transaction
@ -36,7 +36,7 @@ object NotaryChangeProtocol: AbstractStateReplacementProtocol<Party>() {
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal<Party> override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal<Party>
= Proposal(stateRef, modification, stx) = Proposal(stateRef, modification, stx)
override fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> { override fun assembleTx(): Pair<SignedTransaction, List<PublicKeyTree>> {
val state = originalState.state val state = originalState.state
val newState = state.withNotary(modification) val newState = state.withNotary(modification)
val participants = state.data.participants val participants = state.data.participants

View File

@ -1,21 +1,16 @@
package net.corda.protocols package net.corda.protocols
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
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.node.services.TimestampChecker import net.corda.core.node.services.TimestampChecker
import net.corda.core.node.services.UniquenessException import net.corda.core.node.services.UniquenessException
import net.corda.core.node.services.UniquenessProvider import net.corda.core.node.services.UniquenessProvider
import net.corda.core.protocols.ProtocolLogic import net.corda.core.protocols.ProtocolLogic
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction 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 java.security.PublicKey
object NotaryProtocol { object NotaryProtocol {
@ -177,5 +172,5 @@ sealed class NotaryError {
class TransactionInvalid : NotaryError() class TransactionInvalid : NotaryError()
class SignaturesMissing(val missingSigners: Set<PublicKey>) : NotaryError() class SignaturesMissing(val missingSigners: Set<PublicKeyTree>) : NotaryError()
} }

View File

@ -1,11 +1,10 @@
package net.corda.protocols package net.corda.protocols
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.TransientProperty import net.corda.core.contracts.ContractState
import net.corda.core.contracts.* import net.corda.core.contracts.DealState
import net.corda.core.crypto.DigitalSignature import net.corda.core.contracts.StateRef
import net.corda.core.crypto.Party import net.corda.core.crypto.*
import net.corda.core.crypto.signWithECDSA
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.recordTransactions import net.corda.core.node.recordTransactions
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
@ -17,9 +16,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.trace import net.corda.core.utilities.trace
import java.math.BigDecimal
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.
@ -42,7 +39,7 @@ object TwoPartyDealProtocol {
} }
// This object is serialised to the network and is the first protocol message the seller sends to the buyer. // This object is serialised to the network and is the first protocol message the seller sends to the buyer.
data class Handshake<out T>(val payload: T, val publicKey: PublicKey) data class Handshake<out T>(val payload: T, val publicKey: PublicKeyTree)
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.LegallyIdentifiable) class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.LegallyIdentifiable)
@ -90,7 +87,7 @@ object TwoPartyDealProtocol {
progressTracker.currentStep = AWAITING_PROPOSAL progressTracker.currentStep = AWAITING_PROPOSAL
// Make the first message we'll send to kick off the protocol. // Make the first message we'll send to kick off the protocol.
val hello = Handshake(payload, myKeyPair.public) val hello = Handshake(payload, myKeyPair.public.tree)
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello) val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
return maybeSTX return maybeSTX
@ -104,7 +101,7 @@ object TwoPartyDealProtocol {
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, notaryNode.notaryIdentity.owningKey) val wtx: WireTransaction = stx.verifySignatures(myKeyPair.public.tree, notaryNode.notaryIdentity.owningKey)
logger.trace { "Received partially signed transaction: ${stx.id}" } logger.trace { "Received partially signed transaction: ${stx.id}" }
checkDependencies(stx) checkDependencies(stx)
@ -251,18 +248,18 @@ object TwoPartyDealProtocol {
return sendAndReceive<SignaturesFromPrimary>(otherParty, stx).unwrap { it } return sendAndReceive<SignaturesFromPrimary>(otherParty, stx).unwrap { it }
} }
private fun signWithOurKeys(signingPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction { private fun signWithOurKeys(signingPubKeys: List<PublicKeyTree>, 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 (k in signingPubKeys) { for (publicKey in signingPubKeys.keys) {
val priv = serviceHub.keyManagementService.toPrivate(k) val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
ptx.signWith(KeyPair(k, priv)) ptx.signWith(KeyPair(publicKey, privateKey))
} }
return ptx.toSignedTransaction(checkSufficientSignatures = false) return ptx.toSignedTransaction(checkSufficientSignatures = false)
} }
@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<PublicKey>> @Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<PublicKeyTree>>
} }
@ -295,7 +292,7 @@ object TwoPartyDealProtocol {
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<PublicKey>> { override fun assembleSharedTX(handshake: Handshake<AutoOffer>): Pair<TransactionBuilder, List<PublicKeyTree>> {
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,14 @@
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.PublicKeyTree
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
import net.corda.testing.* import net.corda.testing.MEGA_CORP
import net.corda.testing.ledger
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
@ -42,7 +44,7 @@ class TransactionEncumbranceTests {
data class State( data class State(
val validFrom: Instant val validFrom: Instant
) : ContractState { ) : ContractState {
override val participants: List<PublicKey> = emptyList() override val participants: List<PublicKeyTree> = emptyList()
override val contract: Contract = TEST_TIMELOCK_ID override val contract: Contract = TEST_TIMELOCK_ID
} }
} }

View File

@ -1,12 +1,13 @@
package net.corda.core.contracts package net.corda.core.contracts
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.crypto.tree
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
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 net.corda.testing.node.MockTransactionStorage
import net.corda.testing.MEGA_CORP_KEY import net.corda.testing.MEGA_CORP_KEY
import net.corda.testing.node.MockTransactionStorage
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -31,7 +32,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) addCommand(command, signer.public.tree)
signWith(signer) signWith(signer)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction(false) }.toSignedTransaction(false)

View File

@ -4,6 +4,7 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
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.signWithECDSA import net.corda.core.crypto.signWithECDSA
import net.corda.core.crypto.tree
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -29,7 +30,7 @@ class TransactionTests {
outputs = emptyList(), outputs = emptyList(),
commands = emptyList(), commands = emptyList(),
notary = DUMMY_NOTARY, notary = DUMMY_NOTARY,
signers = listOf(DUMMY_KEY_1.public, DUMMY_KEY_2.public), signers = listOf(DUMMY_KEY_1.public.tree, DUMMY_KEY_2.public.tree),
type = TransactionType.General(), type = TransactionType.General(),
timestamp = null timestamp = null
) )
@ -38,20 +39,20 @@ class TransactionTests {
assertFailsWith<IllegalArgumentException> { make().verifySignatures() } assertFailsWith<IllegalArgumentException> { make().verifySignatures() }
assertEquals( assertEquals(
setOf(DUMMY_KEY_1.public), setOf(DUMMY_KEY_1.public.tree),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_2).verifySignatures() }.missing assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_2).verifySignatures() }.missing
) )
assertEquals( assertEquals(
setOf(DUMMY_KEY_2.public), setOf(DUMMY_KEY_2.public.tree),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_1).verifySignatures() }.missing assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_1).verifySignatures() }.missing
) )
assertEquals( assertEquals(
setOf(DUMMY_KEY_2.public), setOf(DUMMY_KEY_2.public.tree),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public) }.missing assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public.tree) }.missing
) )
make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public) make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public.tree)
make(DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public) make(DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public.tree)
make(DUMMY_KEY_1, DUMMY_KEY_2).verifySignatures() make(DUMMY_KEY_1, DUMMY_KEY_2).verifySignatures()
} }
@ -64,7 +65,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) val signers = listOf(DUMMY_NOTARY_KEY.public.tree)
val timestamp: Timestamp? = null val timestamp: Timestamp? = null
val transaction: LedgerTransaction = LedgerTransaction( val transaction: LedgerTransaction = LedgerTransaction(
inputs, inputs,
@ -91,7 +92,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) val signers = listOf(DUMMY_NOTARY_KEY.public.tree)
val timestamp: Timestamp? = null val timestamp: Timestamp? = null
val transaction: LedgerTransaction = LedgerTransaction( val transaction: LedgerTransaction = LedgerTransaction(
inputs, inputs,

View File

@ -2,6 +2,7 @@ package net.corda.core.node
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.PublicKeyTree
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
import net.corda.core.serialization.* import net.corda.core.serialization.*
@ -15,7 +16,6 @@ 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
@ -37,7 +37,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<PublicKey> override val participants: List<PublicKeyTree>
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.PublicKeyTree
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<PublicKey> override val participants: List<PublicKeyTree>
get() = emptyList() get() = emptyList()
override val contract = VaultUpdateTests.DummyContract override val contract = VaultUpdateTests.DummyContract
} }

View File

@ -1,7 +1,9 @@
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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.tree
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.*
@ -9,7 +11,6 @@ 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
@ -27,12 +28,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: PublicKey) : OwnableState { override val owner: PublicKeyTree) : OwnableState {
override val contract: Contract = TEST_PROGRAM_ID override val contract: Contract = TEST_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
} }
interface Commands : CommandData { interface Commands : CommandData {
class Move() : TypeOnlyCommandData(), Commands class Move() : TypeOnlyCommandData(), Commands
@ -45,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), DUMMY_NOTARY) val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public.tree), DUMMY_NOTARY)
lateinit var tx: TransactionBuilder lateinit var tx: TransactionBuilder
@ -53,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)) inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(DUMMY_KEY_1.public.tree))
) )
} }
@ -92,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)) Command(TestCash.Commands.Move(), DUMMY_KEY_2.public.tree))
tx2.signWith(DUMMY_NOTARY_KEY) tx2.signWith(DUMMY_NOTARY_KEY)
tx2.signWith(DUMMY_KEY_2) tx2.signWith(DUMMY_KEY_2)

View File

@ -2,10 +2,9 @@ package net.corda.contracts
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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.time.LocalDate import java.time.LocalDate
import java.time.ZoneOffset import java.time.ZoneOffset
import java.util.* import java.util.*
@ -44,24 +43,24 @@ class AccountReceivable : Contract {
data class State( data class State(
// technical variables // technical variables
override val owner: PublicKey, override val owner: PublicKeyTree,
val status: StatusEnum, val status: StatusEnum,
val props: AccountReceivableProperties val props: AccountReceivableProperties
) : OwnableState { ) : OwnableState {
override val contract = ACCOUNTRECEIVABLE_PROGRAM_ID override val contract = ACCOUNTRECEIVABLE_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = listOf(owner) get() = listOf(owner)
override fun toString() = "AR owned by ${owner.toStringShort()})" override fun toString() = "AR owned by $owner)"
fun checkInvoice(invoice: Invoice.State): Boolean { fun checkInvoice(invoice: Invoice.State): Boolean {
val arProps = Helper.invoicePropsToARProps(invoice.props, props.discountRate) val arProps = Helper.invoicePropsToARProps(invoice.props, props.discountRate)
return props == arProps return props == arProps
} }
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Issue(), copy(owner = newOwner, status = StatusEnum.Issued)) override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Issue(), copy(owner = newOwner, status = StatusEnum.Issued))
} }

View File

@ -2,10 +2,10 @@ package net.corda.contracts
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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.days import net.corda.core.days
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
@ -46,15 +46,15 @@ class BillOfLadingAgreement : Contract {
data class State( data class State(
// technical variables // technical variables
override val owner: PublicKey, override val owner: PublicKeyTree,
val beneficiary: Party, val beneficiary: Party,
val props: BillOfLadingProperties val props: BillOfLadingProperties
) : OwnableState { ) : OwnableState {
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = listOf(owner) get() = listOf(owner)
override fun withNewOwner(newOwner: PublicKey): Pair<CommandData, OwnableState> { override fun withNewOwner(newOwner: PublicKeyTree): Pair<CommandData, OwnableState> {
return Pair(Commands.TransferPossession(), copy(owner = newOwner)) return Pair(Commands.TransferPossession(), copy(owner = newOwner))
} }
@ -112,7 +112,7 @@ class BillOfLadingAgreement : Contract {
/** /**
* Returns a transaction that issues a Bill of Lading Agreement * Returns a transaction that issues a Bill of Lading Agreement
*/ */
fun generateIssue(owner: PublicKey, beneficiary: Party, props: BillOfLadingProperties, notary: Party): TransactionBuilder { fun generateIssue(owner: PublicKeyTree, beneficiary: Party, props: BillOfLadingProperties, notary: Party): TransactionBuilder {
val state = State(owner, beneficiary, props) val state = State(owner, beneficiary, props)
val builder = TransactionType.General.Builder(notary = notary) val builder = TransactionType.General.Builder(notary = notary)
builder.setTime(Instant.now(), 1.days) builder.setTime(Instant.now(), 1.days)
@ -122,17 +122,17 @@ class BillOfLadingAgreement : 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 generateTransferAndEndorse(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKey, newBeneficiary: Party) { fun generateTransferAndEndorse(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKeyTree, newBeneficiary: Party) {
tx.addInputState(BoL) tx.addInputState(BoL)
tx.addOutputState(BoL.state.data.copy(owner = newOwner, beneficiary = newBeneficiary)) tx.addOutputState(BoL.state.data.copy(owner = newOwner, beneficiary = newBeneficiary))
val signers: List<PublicKey> = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey) val signers: List<PublicKeyTree> = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey)
tx.addCommand(Commands.TransferAndEndorseBL(), signers) tx.addCommand(Commands.TransferAndEndorseBL(), signers)
} }
/** /**
* 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 generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKey) { fun generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKeyTree) {
tx.addInputState(BoL) tx.addInputState(BoL)
tx.addOutputState(BoL.state.data.copy(owner = newOwner)) tx.addOutputState(BoL.state.data.copy(owner = newOwner))
// tx.addOutputState(BoL.state.data.copy().withNewOwner(newOwner)) // tx.addOutputState(BoL.state.data.copy().withNewOwner(newOwner))

View File

@ -3,6 +3,7 @@ package net.corda.contracts
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
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.PublicKeyTree
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 import java.security.PublicKey
@ -71,7 +72,7 @@ class Invoice : Contract {
override val contract = INVOICE_PROGRAM_ID override val contract = INVOICE_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = listOf(owner.owningKey) get() = listOf(owner.owningKey)
// returns true when the actual business properties of the // returns true when the actual business properties of the
@ -86,7 +87,7 @@ class Invoice : Contract {
val amount: Amount<Issued<Currency>> get() = props.amount val amount: Amount<Issued<Currency>> get() = props.amount
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean { override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return owner.owningKey in ourKeys || buyer.owningKey in ourKeys return owner.owningKey.containsAny(ourKeys) || buyer.owningKey.containsAny(ourKeys)
} }
} }

View File

@ -1,11 +1,11 @@
package net.corda.contracts package net.corda.contracts
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.NullPublicKey import net.corda.core.crypto.NullPublicKeyTree
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
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
import java.time.LocalDate import java.time.LocalDate
import java.time.Period import java.time.Period
import java.util.* import java.util.*
@ -108,14 +108,14 @@ class LCApplication : Contract {
} }
data class State( data class State(
val owner: PublicKey, val owner: PublicKeyTree,
val status: Status, val status: Status,
val props: LCApplicationProperties val props: LCApplicationProperties
) : ContractState { ) : ContractState {
override val contract = LC_APPLICATION_PROGRAM_ID override val contract = LC_APPLICATION_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = listOf(owner) get() = listOf(owner)
// returns true when the actual business properties of the // returns true when the actual business properties of the
@ -125,7 +125,7 @@ class LCApplication : Contract {
} }
// iterate over the goods list and sum up the price for each // iterate over the goods list and sum up the price for each
fun withoutOwner() = copy(owner = NullPublicKey) fun withoutOwner() = copy(owner = NullPublicKeyTree)
} }
fun generateApply(props: LCApplicationProperties, notary: Party, purchaseOrder: Attachment): TransactionBuilder { fun generateApply(props: LCApplicationProperties, notary: Party, purchaseOrder: Attachment): TransactionBuilder {

View File

@ -3,10 +3,10 @@ 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.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.days import net.corda.core.days
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.Period import java.time.Period
@ -58,7 +58,7 @@ class LOC : Contract {
) : ContractState { ) : ContractState {
override val contract = LOC_PROGRAM_ID override val contract = LOC_PROGRAM_ID
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = listOf() get() = listOf()
} }

View File

@ -2,10 +2,10 @@ package net.corda.contracts.universal
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.PublicKeyTree
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
/** /**
@ -18,7 +18,7 @@ val UNIVERSAL_PROGRAM_ID = UniversalContract()
class UniversalContract : Contract { class UniversalContract : Contract {
data class State(override val participants: List<PublicKey>, data class State(override val participants: List<PublicKeyTree>,
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: PublicKey) { fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKeyTree) {
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

@ -2,15 +2,11 @@ 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.Amount
import net.corda.core.contracts.Frequency import net.corda.core.contracts.Frequency
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import com.sun.org.apache.xpath.internal.operations.Bool import net.corda.core.crypto.PublicKeyTree
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.*
/** /**
* Created by sofusmortensen on 23/05/16. * Created by sofusmortensen on 23/05/16.
@ -20,44 +16,44 @@ fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 6
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24) fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> = private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKeyTree> =
when (arrangement) { when (arrangement) {
is Zero -> ImmutableSet.of<PublicKey>() is Zero -> ImmutableSet.of<PublicKeyTree>()
is Transfer -> ImmutableSet.of(arrangement.from.owningKey) is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
is And -> is And ->
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() arrangement.arrangements.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
is Actions -> is Actions ->
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() arrangement.actions.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
is RollOut -> liablePartiesVisitor(arrangement.template) is RollOut -> liablePartiesVisitor(arrangement.template)
is Continuation -> ImmutableSet.of<PublicKey>() is Continuation -> ImmutableSet.of<PublicKeyTree>()
else -> throw IllegalArgumentException("liableParties " + arrangement) else -> throw IllegalArgumentException("liableParties " + arrangement)
} }
private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKey> = private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKeyTree> =
if (action.actors.size != 1) if (action.actors.size != 1)
liablePartiesVisitor(action.arrangement) liablePartiesVisitor(action.arrangement)
else else
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy() Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy()
/** 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<PublicKey> = liablePartiesVisitor(contract) fun liableParties(contract: Arrangement): Set<PublicKeyTree> = liablePartiesVisitor(contract)
private fun involvedPartiesVisitor(action: Action): Set<PublicKey> = private fun involvedPartiesVisitor(action: Action): Set<PublicKeyTree> =
Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy() Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy()
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> = private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKeyTree> =
when (arrangement) { when (arrangement) {
is Zero -> ImmutableSet.of<PublicKey>() is Zero -> ImmutableSet.of<PublicKeyTree>()
is Transfer -> ImmutableSet.of(arrangement.from.owningKey) is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
is And -> is And ->
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() arrangement.arrangements.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
is Actions -> is Actions ->
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() arrangement.actions.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
else -> throw IllegalArgumentException() else -> throw IllegalArgumentException()
} }
/** returns list of involved parties for a given contract */ /** returns list of involved parties for a given contract */
fun involvedParties(arrangement: Arrangement): Set<PublicKey> = involvedPartiesVisitor(arrangement) fun involvedParties(arrangement: Arrangement): Set<PublicKeyTree> = involvedPartiesVisitor(arrangement)
fun replaceParty(action: Action, from: Party, to: Party): Action = fun replaceParty(action: Action, from: Party, to: Party): Action =
if (action.actors.contains(from)) { if (action.actors.contains(from)) {

View File

@ -1,18 +1,10 @@
/*
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
* set forth therein.
*
* All other rights reserved.
*/
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.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
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.
@ -21,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<PublicKey> override val participants: List<PublicKeyTree>
get() = emptyList() get() = emptyList()
} }

View File

@ -1,10 +1,13 @@
package net.corda.contracts; package net.corda.contracts;
import net.corda.core.contracts.*; import net.corda.core.contracts.Amount;
import net.corda.core.contracts.ContractState;
import net.corda.core.contracts.Issued;
import net.corda.core.contracts.PartyAndReference;
import net.corda.core.crypto.PublicKeyTree;
import java.security.*; import java.time.Instant;
import java.time.*; import java.util.Currency;
import java.util.*;
/* This is an interface solely created to demonstrate that the same kotlin tests can be run against /* This is an interface solely created to demonstrate that the same kotlin tests can be run against
* either a Java implementation of the CommercialPaper or a kotlin implementation. * either a Java implementation of the CommercialPaper or a kotlin implementation.
@ -12,7 +15,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(PublicKey newOwner); ICommercialPaperState withOwner(PublicKeyTree newOwner);
ICommercialPaperState withIssuance(PartyAndReference newIssuance); ICommercialPaperState withIssuance(PartyAndReference newIssuance);

View File

@ -1,24 +1,34 @@
package net.corda.contracts; package net.corda.contracts;
import com.google.common.collect.*; import com.google.common.collect.ImmutableList;
import net.corda.contracts.asset.*; import kotlin.Pair;
import kotlin.Unit;
import net.corda.contracts.asset.CashKt;
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.contracts.Timestamp; import net.corda.core.contracts.TransactionForContract.InOutGroup;
import net.corda.core.contracts.TransactionForContract.*; import net.corda.core.contracts.clauses.AnyComposition;
import net.corda.core.contracts.clauses.*; import net.corda.core.contracts.clauses.Clause;
import net.corda.core.node.services.*; import net.corda.core.contracts.clauses.ClauseVerifier;
import net.corda.core.transactions.*; import net.corda.core.contracts.clauses.GroupClauseVerifier;
import kotlin.*; import net.corda.core.crypto.CryptoUtilitiesKt;
import net.corda.core.crypto.*; import net.corda.core.crypto.Party;
import org.jetbrains.annotations.*; import net.corda.core.crypto.PublicKeyTree;
import net.corda.core.crypto.SecureHash;
import net.corda.core.node.services.VaultService;
import net.corda.core.transactions.TransactionBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.security.*; import java.time.Instant;
import java.time.*; import java.util.Collections;
import java.util.*; import java.util.Currency;
import java.util.stream.*; import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static net.corda.core.contracts.ContractsDSL.*; import static kotlin.collections.CollectionsKt.single;
import static kotlin.collections.CollectionsKt.*; import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat;
/** /**
@ -30,14 +40,14 @@ public class JavaCommercialPaper implements Contract {
public static class State implements OwnableState, ICommercialPaperState { public static class State implements OwnableState, ICommercialPaperState {
private PartyAndReference issuance; private PartyAndReference issuance;
private PublicKey owner; private PublicKeyTree 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, PublicKey owner, Amount<Issued<Currency>> faceValue, public State(PartyAndReference issuance, PublicKeyTree owner, Amount<Issued<Currency>> faceValue,
Instant maturityDate) { Instant maturityDate) {
this.issuance = issuance; this.issuance = issuance;
this.owner = owner; this.owner = owner;
@ -49,13 +59,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(PublicKey newOwner) { public ICommercialPaperState withOwner(PublicKeyTree 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 PublicKey newOwner) { public Pair<CommandData, OwnableState> withNewOwner(@NotNull PublicKeyTree 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));
} }
@ -76,7 +86,7 @@ public class JavaCommercialPaper implements Contract {
} }
@NotNull @NotNull
public PublicKey getOwner() { public PublicKeyTree getOwner() {
return owner; return owner;
} }
@ -117,12 +127,12 @@ public class JavaCommercialPaper implements Contract {
} }
public State withoutOwner() { public State withoutOwner() {
return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate); return new State(issuance, CryptoUtilitiesKt.getNullPublicKeyTree(), faceValue, maturityDate);
} }
@NotNull @NotNull
@Override @Override
public List<PublicKey> getParticipants() { public List<PublicKeyTree> getParticipants() {
return ImmutableList.of(this.owner); return ImmutableList.of(this.owner);
} }
@ -311,7 +321,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, PublicKey newOwner) { public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKeyTree 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())); tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary()));
tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner())); tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner()));

View File

@ -1,8 +1,5 @@
package net.corda.contracts package net.corda.contracts
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.FungibleAsset
import net.corda.core.contracts.InsufficientBalanceException
import net.corda.contracts.asset.sumCashBy import net.corda.contracts.asset.sumCashBy
import net.corda.contracts.clause.AbstractIssue import net.corda.contracts.clause.AbstractIssue
import net.corda.core.contracts.* import net.corda.core.contracts.*
@ -11,9 +8,8 @@ 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.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toBase58String
import net.corda.core.crypto.toStringShort
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
@ -22,7 +18,6 @@ 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.*
@ -64,22 +59,22 @@ class CommercialPaper : Contract {
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
override val owner: PublicKey, override val owner: PublicKeyTree,
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<PublicKey> override val participants: List<PublicKeyTree>
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: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by ${owner.toStringShort()})" 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: PublicKey): ICommercialPaperState = copy(owner = newOwner) override fun withOwner(newOwner: PublicKeyTree): ICommercialPaperState = copy(owner = newOwner)
override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance) override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance)
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue) override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
@ -205,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: PublicKey) { fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKeyTree) {
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)
@ -228,8 +223,8 @@ class CommercialPaper : Contract {
} }
} }
infix fun CommercialPaper.State.`owned by`(owner: PublicKey) = copy(owner = owner) infix fun CommercialPaper.State.`owned by`(owner: PublicKeyTree) = 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: PublicKey) = withOwner(newOwner) infix fun ICommercialPaperState.`owned by`(newOwner: PublicKeyTree) = withOwner(newOwner)

View File

@ -1,18 +1,14 @@
package net.corda.contracts package net.corda.contracts
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.InsufficientBalanceException
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.NullPublicKey import net.corda.core.crypto.NullPublicKeyTree
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.node.services.Vault
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.*
@ -30,19 +26,19 @@ class CommercialPaperLegacy : Contract {
data class State( data class State(
val issuance: PartyAndReference, val issuance: PartyAndReference,
override val owner: PublicKey, override val owner: PublicKeyTree,
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 = NullPublicKey) fun withoutOwner() = copy(owner = NullPublicKeyTree)
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by ${owner.toStringShort()})" 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: PublicKey): ICommercialPaperState = copy(owner = newOwner) override fun withOwner(newOwner: PublicKeyTree): ICommercialPaperState = copy(owner = newOwner)
override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance) override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance)
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue) override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
@ -119,7 +115,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: PublicKey) { fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKeyTree) {
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

@ -9,16 +9,13 @@ import net.corda.core.contracts.clauses.FirstComposition
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.* import net.corda.core.crypto.*
import net.corda.core.node.services.Vault
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.core.schemas.QueryableState 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.CashSchemaV1 import net.corda.schemas.CashSchemaV1
import net.corda.core.crypto.*
import java.math.BigInteger import java.math.BigInteger
import java.security.PublicKey
import java.util.* import java.util.*
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -85,22 +82,22 @@ 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: PublicKey, override val owner: PublicKeyTree,
override val encumbrance: Int? = null override val encumbrance: Int? = null
) : FungibleAsset<Currency>, QueryableState { ) : FungibleAsset<Currency>, QueryableState {
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKey) constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKeyTree)
: 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: PublicKey): FungibleAsset<Currency> override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKeyTree): FungibleAsset<Currency>
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner) = copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by ${owner.toStringShort()})" override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKeyTree) = 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 {
@ -148,13 +145,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: PublicKey, notary: Party) fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKeyTree, 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: PublicKey, notary: Party) { fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKeyTree, 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 +159,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: PublicKey) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKeyTree)
= 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)
override fun generateIssueCommand() = Commands.Issue() override fun generateIssueCommand() = Commands.Issue()
@ -179,7 +176,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: PublicKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow() fun Iterable<ContractState>.sumCashBy(owner: PublicKeyTree): 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
@ -195,12 +192,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: PublicKey) = copy(owner = owner) fun Cash.State.ownedBy(owner: PublicKeyTree) = copy(owner = owner)
fun Cash.State.issuedBy(party: Party) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party)))) fun Cash.State.issuedBy(party: Party) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party))))
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: PublicKey) = ownedBy(owner) infix fun Cash.State.`owned by`(owner: PublicKeyTree) = ownedBy(owner)
infix fun Cash.State.`issued by`(party: Party) = issuedBy(party) infix fun Cash.State.`issued by`(party: Party) = 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)
@ -210,8 +207,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).ref(1) } val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public.tree).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)), NullPublicKey) val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKeyTree)
/** 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, NullPublicKey) val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKeyTree)

View File

@ -4,15 +4,14 @@ import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.contracts.clause.AbstractIssue import net.corda.contracts.clause.AbstractIssue
import net.corda.contracts.clause.NoZeroSizedOutputs import net.corda.contracts.clause.NoZeroSizedOutputs
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.AnyComposition import net.corda.core.contracts.clauses.AnyComposition
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.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
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.crypto.toStringShort
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.util.* import java.util.*
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -90,27 +89,27 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
*/ */
class ConserveAmount : AbstractConserveAmount<State, Commands, Commodity>() class ConserveAmount : AbstractConserveAmount<State, Commands, Commodity>()
} }
/** A state representing a commodity claim against some party */ /** A state representing a commodity claim against some party */
data class State( data class State(
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: PublicKey override val owner: PublicKeyTree
) : FungibleAsset<Commodity> { ) : FungibleAsset<Commodity> {
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKey) constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKeyTree)
: 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: PublicKey): FungibleAsset<Commodity> override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKeyTree): FungibleAsset<Commodity>
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner) = copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by ${owner.toStringShort()})" override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)"
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
} }
// Just for grouping // Just for grouping
@ -144,13 +143,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: PublicKey, notary: Party) fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKeyTree, 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: PublicKey, notary: Party) { fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKeyTree, 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
@ -159,7 +158,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
} }
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKey) override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKeyTree)
= 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)
override fun generateIssueCommand() = Commands.Issue() override fun generateIssueCommand() = Commands.Issue()

View File

@ -1,16 +1,18 @@
package net.corda.contracts.asset package net.corda.contracts.asset
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import net.corda.contracts.asset.Obligation.Lifecycle.NORMAL
import net.corda.contracts.clause.* import net.corda.contracts.clause.*
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.* import net.corda.core.contracts.clauses.*
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.random63BitValue import net.corda.core.random63BitValue
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.* import net.corda.core.utilities.Emoji
import net.corda.core.crypto.* import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.TEST_TX_TIME
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.*
@ -267,21 +269,21 @@ class Obligation<P> : 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: PublicKey val beneficiary: PublicKeyTree
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> { ) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
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<PublicKey> = setOf(beneficiary) override val exitKeys: Collection<PublicKeyTree> = setOf(beneficiary)
val dueBefore: Instant = template.dueBefore val dueBefore: Instant = template.dueBefore
override val participants: List<PublicKey> = listOf(obligor.owningKey, beneficiary) override val participants: List<PublicKeyTree> = listOf(obligor.owningKey, beneficiary)
override val owner: PublicKey = beneficiary override val owner: PublicKeyTree = beneficiary
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKey): State<P> override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKeyTree): State<P>
= copy(quantity = newAmount.quantity, beneficiary = newOwner) = copy(quantity = newAmount.quantity, beneficiary = newOwner)
override fun toString() = when (lifecycle) { override fun toString() = when (lifecycle) {
Lifecycle.NORMAL -> "${Emoji.bagOfCash}Debt($amount due $dueBefore to ${beneficiary.toStringShort()})" Lifecycle.NORMAL -> "${Emoji.bagOfCash}Debt($amount due $dueBefore to $beneficiary)"
Lifecycle.DEFAULTED -> "${Emoji.bagOfCash}Debt($amount unpaid by $dueBefore to ${beneficiary.toStringShort()})" Lifecycle.DEFAULTED -> "${Emoji.bagOfCash}Debt($amount unpaid by $dueBefore to $beneficiary)"
} }
override val bilateralNetState: BilateralNetState<P> override val bilateralNetState: BilateralNetState<P>
@ -309,7 +311,7 @@ class Obligation<P> : Contract {
} }
} }
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(beneficiary = newOwner)) override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(beneficiary = newOwner))
} }
// Just for grouping // Just for grouping
@ -414,7 +416,7 @@ class Obligation<P> : Contract {
* and same parties involved). * and same parties involved).
*/ */
fun generateCloseOutNetting(tx: TransactionBuilder, fun generateCloseOutNetting(tx: TransactionBuilder,
signer: PublicKey, signer: PublicKeyTree,
vararg states: State<P>) { vararg states: State<P>) {
val netState = states.firstOrNull()?.bilateralNetState val netState = states.firstOrNull()?.bilateralNetState
@ -442,7 +444,7 @@ class Obligation<P> : 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>>>): PublicKey assetStates: List<StateAndRef<Obligation.State<P>>>): PublicKeyTree
= 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() },
@ -456,7 +458,7 @@ class Obligation<P> : Contract {
obligor: Party, obligor: Party,
issuanceDef: Terms<P>, issuanceDef: Terms<P>,
pennies: Long, pennies: Long,
beneficiary: PublicKey, beneficiary: PublicKeyTree,
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)
@ -472,7 +474,7 @@ class Obligation<P> : Contract {
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL }) "all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL })
} }
val groups = states.groupBy { it.multilateralNetState } val groups = states.groupBy { it.multilateralNetState }
val partyLookup = HashMap<PublicKey, Party>() val partyLookup = HashMap<PublicKeyTree, Party>()
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.
@ -516,7 +518,7 @@ class Obligation<P> : 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 ((aggregateState, stateAndRefs) in groups) { for ((aggregateState, stateAndRefs) in groups) {
val partiesUsed = ArrayList<PublicKey>() val partiesUsed = ArrayList<PublicKeyTree>()
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)
@ -561,7 +563,7 @@ class Obligation<P> : 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<PublicKey>() val assetSigners = HashSet<PublicKeyTree>()
statesAndRefs.forEach { tx.addInputState(it) } statesAndRefs.forEach { tx.addInputState(it) }
@ -613,8 +615,8 @@ class Obligation<P> : 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> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>> { fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<Obligation.Terms<P>>> {
val balances = HashMap<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>>() val balances = HashMap<Pair<PublicKeyTree, PublicKeyTree>, 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)
@ -628,8 +630,8 @@ fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligat
/** /**
* Net off the amounts due between parties. * Net off the amounts due between parties.
*/ */
fun <P> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<Pair<PublicKey, PublicKey>, Amount<P>> { fun <P> netAmountsDue(balances: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>): Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>> {
val nettedBalances = HashMap<Pair<PublicKey, PublicKey>, Amount<P>>() val nettedBalances = HashMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>()
balances.forEach { balance -> balances.forEach { balance ->
val (obligor, beneficiary) = balance.key val (obligor, beneficiary) = balance.key
@ -653,8 +655,8 @@ fun <P> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map
* @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> sumAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<PublicKey, Long> { fun <P> sumAmountsDue(balances: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>): Map<PublicKeyTree, Long> {
val sum = HashMap<PublicKey, Long>() val sum = HashMap<PublicKeyTree, Long>()
// Fill the map with zeroes initially // Fill the map with zeroes initially
balances.keys.forEach { balances.keys.forEach {
@ -695,19 +697,19 @@ fun <P> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<Obligat
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef) = filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore)) infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T> Obligation.State<T>.between(parties: Pair<Party, PublicKey>) = copy(obligor = parties.first, beneficiary = parties.second) infix fun <T> Obligation.State<T>.between(parties: Pair<Party, PublicKeyTree>) = copy(obligor = parties.first, beneficiary = parties.second)
infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKey) = copy(beneficiary = owner) infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKeyTree) = copy(beneficiary = owner)
infix fun <T> Obligation.State<T>.`issued by`(party: Party) = copy(obligor = party) infix fun <T> Obligation.State<T>.`issued by`(party: Party) = copy(obligor = party)
// For Java users: // For Java users:
@Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: PublicKey) = copy(beneficiary = owner) @Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: PublicKeyTree) = copy(beneficiary = owner)
@Suppress("unused") fun <T> Obligation.State<T>.issuedBy(party: Party) = copy(obligor = party) @Suppress("unused") fun <T> Obligation.State<T>.issuedBy(party: Party) = 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) } val DUMMY_OBLIGATION_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_OBLIGATION_ISSUER_KEY.public.tree) }
val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency> val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME) get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency> val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NullPublicKey) get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NullPublicKeyTree)

View File

@ -2,9 +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.Party import net.corda.core.crypto.PublicKeyTree
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// //
@ -40,7 +39,7 @@ abstract class OnLedgerAsset<T : Any, C: CommandData, S : FungibleAsset<T>> : Co
* @return the public key of the assets issuer, who must sign the transaction for it to be valid. * @return the public key of the assets issuer, who must sign the transaction for it to be valid.
*/ */
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>, fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>): PublicKey assetStates: List<StateAndRef<S>>): PublicKeyTree
= conserveClause.generateExit(tx, amountIssued, assetStates, = conserveClause.generateExit(tx, amountIssued, assetStates,
deriveState = { state, amount, owner -> deriveState(state, amount, owner) }, deriveState = { state, amount, owner -> deriveState(state, amount, owner) },
generateMoveCommand = { -> generateMoveCommand() }, generateMoveCommand = { -> generateMoveCommand() },
@ -56,5 +55,5 @@ abstract class OnLedgerAsset<T : Any, C: CommandData, S : FungibleAsset<T>> : Co
* 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: PublicKey): TransactionState<S> abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKeyTree): 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.PublicKeyTree
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.security.PublicKey
import java.util.* import java.util.*
/** /**
@ -47,9 +47,9 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
*/ */
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>>, PublicKey) -> TransactionState<S>, deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKeyTree) -> TransactionState<S>,
generateMoveCommand: () -> CommandData, generateMoveCommand: () -> CommandData,
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey { generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKeyTree {
val owner = assetStates.map { it.state.data.owner }.toSet().single() val owner = assetStates.map { it.state.data.owner }.toSet().single()
val currency = amountIssued.token.product val currency = amountIssued.token.product
val amount = Amount(amountIssued.quantity, currency) val amount = Amount(amountIssued.quantity, currency)
@ -92,7 +92,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<PublicKey> = inputs.flatMap { it.exitKeys }.toSet() val exitKeys: Set<PublicKeyTree> = 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 java.security.PublicKey import net.corda.core.crypto.PublicKeyTree
/** /**
* 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> {
* Bilateral states are used in close-out netting. * Bilateral states are used in close-out netting.
*/ */
data class BilateralNetState<P>( data class BilateralNetState<P>(
val partyKeys: Set<PublicKey>, val partyKeys: Set<PublicKeyTree>,
override val template: Obligation.Terms<P> override val template: Obligation.Terms<P>
) : NetState<P> ) : NetState<P>

View File

@ -5,7 +5,10 @@ import com.pholser.junit.quickcheck.generator.Generator
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
import com.pholser.junit.quickcheck.random.SourceOfRandomness import com.pholser.junit.quickcheck.random.SourceOfRandomness
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.* import net.corda.core.contracts.Command
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.TransactionType
import net.corda.core.crypto.NullSignature import net.corda.core.crypto.NullSignature
import net.corda.core.testing.* import net.corda.core.testing.*
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -22,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 = PublicKeyGenerator().generate(random, status) owner = PublicKeyTreeGenerator().generate(random, status)
) )
} }
} }
@ -55,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(PublicKeyGenerator())) signersGenerator.addComponentGenerators(listOf(PublicKeyTreeGenerator()))
return Command(CommandDataGenerator().generate(random, status), PublicKeyGenerator().generate(random, status)) return Command(CommandDataGenerator().generate(random, status), PublicKeyTreeGenerator().generate(random, status))
} }
} }

View File

@ -7,16 +7,15 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.core.contracts.Amount 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.transactions.SignedTransaction
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.PublicKeyTree
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.protocols.StateMachineRunId
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
@ -36,12 +35,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: PublicKey? = null, ownedBy: PublicKeyTree? = null,
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER, issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault { issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault {
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng) val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
val myKey: PublicKey = ownedBy ?: myInfo.legalIdentity.owningKey val myKey: PublicKeyTree = 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

@ -1,14 +1,10 @@
package net.corda.protocols package net.corda.protocols
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash
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.DigitalSignature import net.corda.core.crypto.*
import net.corda.core.crypto.Party
import net.corda.core.crypto.signWithECDSA
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.services.ServiceType
import net.corda.core.protocols.ProtocolLogic import net.corda.core.protocols.ProtocolLogic
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -17,7 +13,6 @@ import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
/** /**
@ -56,7 +51,7 @@ object TwoPartyTradeProtocol {
data class SellerTradeInfo( data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>, val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>, val price: Amount<Currency>,
val sellerOwnerKey: PublicKey val sellerOwnerKey: PublicKeyTree
) )
data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey, data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey,
@ -104,8 +99,9 @@ object TwoPartyTradeProtocol {
private fun receiveAndCheckProposedTransaction(): SignedTransaction { private fun receiveAndCheckProposedTransaction(): SignedTransaction {
progressTracker.currentStep = AWAITING_PROPOSAL progressTracker.currentStep = AWAITING_PROPOSAL
val myPublicKey = myKeyPair.public.tree
// Make the first message we'll send to kick off the protocol. // Make the first message we'll send to kick off the protocol.
val hello = SellerTradeInfo(assetToSell, price, myKeyPair.public) val hello = SellerTradeInfo(assetToSell, price, myPublicKey)
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello) val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
@ -115,14 +111,14 @@ object TwoPartyTradeProtocol {
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 = it.verifySignatures(myKeyPair.public, notaryNode.notaryIdentity.owningKey) val wtx: WireTransaction = it.verifySignatures(myPublicKey, notaryNode.notaryIdentity.owningKey)
logger.trace { "Received partially signed transaction: ${it.id}" } logger.trace { "Received partially signed transaction: ${it.id}" }
// Download and check all the things that this transaction depends on and verify it is contract-valid, // Download and check all the things that this transaction depends on and verify it is contract-valid,
// even though it is missing signatures. // even though it is missing signatures.
subProtocol(ResolveTransactionsProtocol(wtx, otherParty)) subProtocol(ResolveTransactionsProtocol(wtx, otherParty))
if (wtx.outputs.map { it.data }.sumCashBy(myKeyPair.public).withoutIssuer() != price) if (wtx.outputs.map { it.data }.sumCashBy(myPublicKey).withoutIssuer() != price)
throw IllegalArgumentException("Transaction is not sending us the right amount of cash") throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
// There are all sorts of funny games a malicious secondary might play here, we should fix them: // There are all sorts of funny games a malicious secondary might play here, we should fix them:
@ -227,17 +223,17 @@ object TwoPartyTradeProtocol {
return sendAndReceive<SignaturesFromSeller>(otherParty, stx).unwrap { it } return sendAndReceive<SignaturesFromSeller>(otherParty, stx).unwrap { it }
} }
private fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction { private fun signWithOurKeys(cashSigningPubKeys: List<PublicKeyTree>, 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 (k in cashSigningPubKeys) { for (publicKey in cashSigningPubKeys.keys) {
val priv = serviceHub.keyManagementService.toPrivate(k) val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
ptx.signWith(KeyPair(k, priv)) ptx.signWith(KeyPair(publicKey, privateKey))
} }
return ptx.toSignedTransaction(checkSufficientSignatures = false) return ptx.toSignedTransaction(checkSufficientSignatures = false)
} }
private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKey>> { private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKeyTree>> {
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
@ -251,7 +247,7 @@ object TwoPartyTradeProtocol {
// 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) val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public.tree)
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

@ -5,6 +5,7 @@ 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.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.tree
import net.corda.core.days import net.corda.core.days
import net.corda.core.node.recordTransactions import net.corda.core.node.recordTransactions
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
@ -19,8 +20,8 @@ import net.corda.core.utilities.TEST_TX_TIME
import net.corda.node.services.vault.NodeVaultService import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.node.utilities.databaseTransaction import net.corda.node.utilities.databaseTransaction
import net.corda.testing.node.MockServices
import net.corda.testing.* import net.corda.testing.*
import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -277,8 +278,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) aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public.tree)
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public) CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public.tree)
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

@ -2,9 +2,7 @@ package 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.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
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
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
@ -22,12 +20,10 @@ import net.corda.testing.node.MockKeyManagementService
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.junit.After
import org.junit.Before 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.*
@ -459,7 +455,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: PublicKey get() = OUR_KEY.public val OUR_PUBKEY_1: PublicKeyTree get() = OUR_KEY.public.tree
val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2 val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2
@ -485,7 +481,7 @@ class CashTests {
return tx.toWireTransaction() return tx.toWireTransaction()
} }
fun makeSpend(amount: Amount<Currency>, dest: PublicKey): WireTransaction { fun makeSpend(amount: Amount<Currency>, dest: PublicKeyTree): 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.NullPublicKey import net.corda.core.crypto.NullPublicKeyTree
import net.corda.core.crypto.PublicKeyTree
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, NullPublicKey) obligationDef, oneUnitFcoj.quantity, NullPublicKeyTree)
// Try settling a simple commodity obligation // Try settling a simple commodity obligation
ledger { ledger {
unverifiedTransaction { unverifiedTransaction {
@ -830,7 +830,7 @@ class ObligationTests {
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<PublicKey, PublicKey>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning val expected: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
val actual = netAmountsDue(balanced) val actual = netAmountsDue(balanced)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -851,8 +851,8 @@ class ObligationTests {
@Test @Test
fun `summing empty balances due between parties`() { fun `summing empty balances due between parties`() {
val empty = emptyMap<Pair<PublicKey, PublicKey>, Amount<Currency>>() val empty = emptyMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<Currency>>()
val expected = emptyMap<PublicKey, Long>() val expected = emptyMap<PublicKeyTree, Long>()
val actual = sumAmountsDue(empty) val actual = sumAmountsDue(empty)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@ -872,7 +872,7 @@ class ObligationTests {
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<PublicKey, Long> = emptyMap() // Zero balances are stripped before returning val expected: Map<PublicKeyTree, Long> = emptyMap() // Zero balances are stripped before returning
val actual = sumAmountsDue(balanced) val actual = sumAmountsDue(balanced)
assertEquals(expected, actual) assertEquals(expected, actual)
} }

View File

@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash
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.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.keys
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.toStringShort
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
@ -101,7 +102,7 @@ class CordaRPCOpsImpl(
val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder, val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder,
req.amount.withoutIssuer(), req.recipient.owningKey, setOf(req.amount.token.issuer.party)) req.amount.withoutIssuer(), req.recipient.owningKey, setOf(req.amount.token.issuer.party))
keysForSigning.forEach { keysForSigning.keys.forEach {
val key = services.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}") val key = services.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

@ -1,9 +1,9 @@
package net.corda.node.services.identity package net.corda.node.services.identity
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
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 java.security.PublicKey
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
@ -12,7 +12,7 @@ import javax.annotation.concurrent.ThreadSafe
*/ */
@ThreadSafe @ThreadSafe
class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService { class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService {
private val keyToParties = ConcurrentHashMap<PublicKey, Party>() private val keyToParties = ConcurrentHashMap<PublicKeyTree, Party>()
private val nameToParties = ConcurrentHashMap<String, Party>() private val nameToParties = ConcurrentHashMap<String, Party>()
override fun registerIdentity(party: Party) { override fun registerIdentity(party: Party) {
@ -20,6 +20,6 @@ class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService {
nameToParties[party.name] = party nameToParties[party.name] = party
} }
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key] override fun partyFromKey(key: PublicKeyTree): Party? = keyToParties[key]
override fun partyFromName(name: String): Party? = nameToParties[name] override fun partyFromName(name: String): Party? = nameToParties[name]
} }

View File

@ -2,12 +2,11 @@ package net.corda.node.services.messaging
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.parsePublicKeyBase58 import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.toBase58String
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.read import net.corda.core.read
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.services.config.NodeSSLConfiguration import net.corda.node.services.config.NodeSSLConfiguration
import net.corda.node.services.config.configureWithDevSSLCertificate import net.corda.node.services.config.configureWithDevSSLCertificate
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
@ -18,7 +17,6 @@ import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
import java.nio.file.FileSystems import java.nio.file.FileSystems
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyStore import java.security.KeyStore
import java.security.PublicKey
/** /**
* The base class for Artemis services that defines shared data structures and transport configuration * The base class for Artemis services that defines shared data structures and transport configuration
@ -77,7 +75,7 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
* may change or evolve and code that relies upon it being a simple host/port may not function correctly. * may change or evolve and code that relies upon it being a simple host/port may not function correctly.
* For instance it may contain onion routing data. * For instance it may contain onion routing data.
*/ */
data class NodeAddress(val identity: PublicKey, override val hostAndPort: HostAndPort) : SingleMessageRecipient, ArtemisAddress { data class NodeAddress(val identity: PublicKeyTree, override val hostAndPort: HostAndPort) : SingleMessageRecipient, ArtemisAddress {
override val queueName: SimpleString by lazy { SimpleString(PEERS_PREFIX+identity.toBase58String()) } override val queueName: SimpleString by lazy { SimpleString(PEERS_PREFIX+identity.toBase58String()) }
override fun toString(): String = "${javaClass.simpleName}(identity = $queueName, $hostAndPort)" override fun toString(): String = "${javaClass.simpleName}(identity = $queueName, $hostAndPort)"
} }
@ -85,9 +83,9 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
/** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */ /** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */
abstract val config: NodeSSLConfiguration abstract val config: NodeSSLConfiguration
protected fun parseKeyFromQueueName(name: String): PublicKey { protected fun parseKeyFromQueueName(name: String): PublicKeyTree {
require(name.startsWith(PEERS_PREFIX)) require(name.startsWith(PEERS_PREFIX))
return parsePublicKeyBase58(name.substring(PEERS_PREFIX.length)) return PublicKeyTree.parseFromBase58(name.substring(PEERS_PREFIX.length))
} }
protected enum class ConnectionDirection { INBOUND, OUTBOUND } protected enum class ConnectionDirection { INBOUND, OUTBOUND }

View File

@ -2,6 +2,7 @@ package net.corda.node.services.messaging
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.opaque import net.corda.core.serialization.opaque
@ -19,7 +20,6 @@ import org.apache.activemq.artemis.api.core.client.*
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.statements.InsertStatement import org.jetbrains.exposed.sql.statements.InsertStatement
import java.security.PublicKey
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
@ -49,7 +49,7 @@ import javax.annotation.concurrent.ThreadSafe
@ThreadSafe @ThreadSafe
class NodeMessagingClient(override val config: NodeConfiguration, class NodeMessagingClient(override val config: NodeConfiguration,
val serverHostPort: HostAndPort, val serverHostPort: HostAndPort,
val myIdentity: PublicKey?, val myIdentity: PublicKeyTree?,
val executor: AffinityExecutor, val executor: AffinityExecutor,
val database: Database) : ArtemisMessagingComponent(), MessagingServiceInternal { val database: Database) : ArtemisMessagingComponent(), MessagingServiceInternal {
companion object { companion object {

View File

@ -9,10 +9,15 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.serializers.JavaSerializer import com.esotericsoftware.kryo.serializers.JavaSerializer
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.ErrorOr import net.corda.core.ErrorOr
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.PhysicalLocation import net.corda.core.node.PhysicalLocation
import net.corda.core.node.ServiceEntry import net.corda.core.node.ServiceEntry
@ -23,9 +28,6 @@ import net.corda.core.serialization.*
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.node.services.User import net.corda.node.services.User
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.core.crypto.*
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.objenesis.strategy.StdInstantiatorStrategy import org.objenesis.strategy.StdInstantiatorStrategy
@ -148,6 +150,8 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
register(ByteArray::class.java) register(ByteArray::class.java)
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer) register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer) register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
register(PublicKeyTree.Leaf::class.java)
register(PublicKeyTree.Node::class.java)
register(Vault::class.java) register(Vault::class.java)
register(Vault.Update::class.java) register(Vault.Update::class.java)
register(StateMachineRunId::class.java) register(StateMachineRunId::class.java)
@ -179,7 +183,7 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
register(ArtemisMessagingComponent.NodeAddress::class.java, register(ArtemisMessagingComponent.NodeAddress::class.java,
read = { kryo, input -> read = { kryo, input ->
ArtemisMessagingComponent.NodeAddress( ArtemisMessagingComponent.NodeAddress(
parsePublicKeyBase58(kryo.readObject(input, String::class.java)), PublicKeyTree.parseFromBase58(kryo.readObject(input, String::class.java)),
kryo.readObject(input, HostAndPort::class.java)) kryo.readObject(input, HostAndPort::class.java))
}, },
write = { kryo, output, nodeAddress -> write = { kryo, output, nodeAddress ->

View File

@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.bufferUntilSubscribed import net.corda.core.bufferUntilSubscribed
import net.corda.core.contracts.Contract import net.corda.core.contracts.Contract
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.crypto.toStringShort import net.corda.core.crypto.PublicKeyTree
import net.corda.core.map import net.corda.core.map
import net.corda.core.messaging.MessagingService import net.corda.core.messaging.MessagingService
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
@ -29,7 +29,6 @@ import net.corda.node.utilities.AddOrRemove
import net.corda.protocols.sendRequest import net.corda.protocols.sendRequest
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
@ -66,14 +65,14 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach
override fun get(serviceType: ServiceType) = registeredNodes.filterValues { it.advertisedServices.any { it.info.type.isSubTypeOf(serviceType) } }.map { it.value } override fun get(serviceType: ServiceType) = registeredNodes.filterValues { it.advertisedServices.any { it.info.type.isSubTypeOf(serviceType) } }.map { it.value }
override fun getRecommended(type: ServiceType, contract: Contract, vararg party: Party): NodeInfo? = get(type).firstOrNull() override fun getRecommended(type: ServiceType, contract: Contract, vararg party: Party): NodeInfo? = get(type).firstOrNull()
override fun getNodeByLegalName(name: String) = get().singleOrNull { it.legalIdentity.name == name } override fun getNodeByLegalName(name: String) = get().singleOrNull { it.legalIdentity.name == name }
override fun getNodeByPublicKey(publicKey: PublicKey): NodeInfo? { override fun getNodeByPublicKey(publicKey: PublicKeyTree): NodeInfo? {
// Although we should never have more than one match, it is theoretically possible. Report an error if it happens. // Although we should never have more than one match, it is theoretically possible. Report an error if it happens.
val candidates = get().filter { val candidates = get().filter {
(it.legalIdentity.owningKey == publicKey) (it.legalIdentity.owningKey == publicKey)
|| it.advertisedServices.any { it.identity.owningKey == publicKey } || it.advertisedServices.any { it.identity.owningKey == publicKey }
} }
if (candidates.size > 1) { if (candidates.size > 1) {
throw IllegalStateException("Found more than one match for key ${publicKey.toStringShort()}") throw IllegalStateException("Found more than one match for key $publicKey")
} }
return candidates.singleOrNull() return candidates.singleOrNull()
} }

View File

@ -1,6 +1,7 @@
package net.corda.node.services.network package net.corda.node.services.network
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import kotlinx.support.jdk8.collections.compute
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
@ -23,7 +24,6 @@ import net.corda.node.services.api.AbstractNodeService
import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AddOrRemove
import net.corda.protocols.ServiceRequestMessage import net.corda.protocols.ServiceRequestMessage
import kotlinx.support.jdk8.collections.compute
import java.security.PrivateKey import java.security.PrivateKey
import java.security.SignatureException import java.security.SignatureException
import java.time.Instant import java.time.Instant
@ -333,7 +333,7 @@ class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemo
*/ */
fun toWire(privateKey: PrivateKey): WireNodeRegistration { fun toWire(privateKey: PrivateKey): WireNodeRegistration {
val regSerialized = this.serialize() val regSerialized = this.serialize()
val regSig = privateKey.signWithECDSA(regSerialized.bits, node.legalIdentity.owningKey) val regSig = privateKey.signWithECDSA(regSerialized.bits, node.legalIdentity.owningKey.singleKey)
return WireNodeRegistration(regSerialized, regSig) return WireNodeRegistration(regSerialized, regSig)
} }
@ -347,7 +347,7 @@ class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemo
class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalSignature.WithKey) : SignedData<NodeRegistration>(raw, sig) { class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalSignature.WithKey) : SignedData<NodeRegistration>(raw, sig) {
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
override fun verifyData(data: NodeRegistration) { override fun verifyData(data: NodeRegistration) {
require(data.node.legalIdentity.owningKey == sig.by) require(data.node.legalIdentity.owningKey.isFulfilledBy(sig.by))
} }
} }

View File

@ -6,6 +6,7 @@ import net.corda.core.ThreadBox
import net.corda.core.bufferUntilSubscribed import net.corda.core.bufferUntilSubscribed
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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
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
@ -155,8 +156,8 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
*/ */
override fun generateSpend(tx: TransactionBuilder, override fun generateSpend(tx: TransactionBuilder,
amount: Amount<Currency>, amount: Amount<Currency>,
to: PublicKey, to: PublicKeyTree,
onlyFromParties: Set<Party>?): Pair<TransactionBuilder, List<PublicKey>> { onlyFromParties: Set<Party>?): Pair<TransactionBuilder, List<PublicKeyTree>> {
// 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.
@ -239,7 +240,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
return Pair(tx, keysList) return Pair(tx, keysList)
} }
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKey) private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKeyTree)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner)) = txState.copy(data = txState.data.copy(amount = amount, owner = owner))
/** /**
@ -292,7 +293,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>): Boolean { private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>): Boolean {
return if (state is OwnableState) { return if (state is OwnableState) {
state.owner in ourKeys state.owner.containsAny(ourKeys)
} else if (state is LinearState) { } else if (state is LinearState) {
// It's potentially of interest to the vault // It's potentially of interest to the vault
state.isRelevant(ourKeys) state.isRelevant(ourKeys)

View File

@ -1,11 +1,12 @@
package net.corda.node.utilities package net.corda.node.utilities
import co.paralleluniverse.strands.Strand import co.paralleluniverse.strands.Strand
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import net.corda.core.crypto.PublicKeyTree
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
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.TransactionInterface import org.jetbrains.exposed.sql.transactions.TransactionInterface
import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.transactions.TransactionManager
@ -137,15 +138,17 @@ class StrandLocalTransactionManager(initWithDatabase: Database) : TransactionMan
} }
// 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<PublicKey>) data class PartyColumns(val name: Column<String>, val owningKey: Column<PublicKeyTree>)
data class StateRefColumns(val txId: Column<SecureHash>, val index: Column<Int>) data class StateRefColumns(val txId: Column<SecureHash>, val index: Column<Int>)
/** /**
* [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.publicKeyTree(name: String) = this.registerColumn<PublicKeyTree>(name, PublicKeyTreeColumnType)
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.publicKey(keyColumnName)) fun Table.party(nameColumnName: String, keyColumnName: String) = PartyColumns(this.varchar(nameColumnName, length = 255), this.publicKeyTree(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)
@ -163,6 +166,15 @@ object PublicKeyColumnType : ColumnType() {
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 [PublicKeyTree].
*/
object PublicKeyTreeColumnType : ColumnType() {
override fun sqlType(): String = "VARCHAR"
override fun valueFromDB(value: Any): Any = PublicKeyTree.parseFromBase58(value.toString())
override fun notNullValueToDB(value: Any): Any = if (value is PublicKeyTree) 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

@ -146,8 +146,9 @@ class CordaRPCOpsImplTest {
require(tx.tx.outputs.size == 1) require(tx.tx.outputs.size == 1)
val signaturePubKeys = tx.sigs.map { it.by }.toSet() val signaturePubKeys = tx.sigs.map { it.by }.toSet()
// Only Alice signed // Only Alice signed
require(signaturePubKeys.size == 1) val aliceKey = aliceNode.info.legalIdentity.owningKey
require(signaturePubKeys.contains(aliceNode.info.legalIdentity.owningKey)) require(signaturePubKeys.size <= aliceKey.keys.size)
require(aliceKey.isFulfilledBy(signaturePubKeys))
}, },
// MOVE // MOVE
expect { tx -> expect { tx ->
@ -155,9 +156,8 @@ class CordaRPCOpsImplTest {
require(tx.tx.outputs.size == 1) require(tx.tx.outputs.size == 1)
val signaturePubKeys = tx.sigs.map { it.by }.toSet() val signaturePubKeys = tx.sigs.map { it.by }.toSet()
// Alice and Notary signed // Alice and Notary signed
require(signaturePubKeys.size == 2) require(aliceNode.info.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys))
require(signaturePubKeys.contains(aliceNode.info.legalIdentity.owningKey)) require(notaryNode.info.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys))
require(signaturePubKeys.contains(notaryNode.info.notaryIdentity.owningKey))
} }
) )
} }

View File

@ -5,7 +5,9 @@ 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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.tree
import net.corda.core.days import net.corda.core.days
import net.corda.core.map import net.corda.core.map
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
@ -40,7 +42,6 @@ import rx.Observable
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import java.util.* import java.util.*
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.Future import java.util.concurrent.Future
@ -250,7 +251,7 @@ class TwoPartyTradeProtocolTests {
} }
val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray())) val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray()))
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public, notaryNode.info.notaryIdentity).second val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public.tree, notaryNode.info.notaryIdentity).second
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode) val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode)
val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
@ -342,7 +343,7 @@ class TwoPartyTradeProtocolTests {
} }
val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray())) val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray()))
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public, notaryNode.info.notaryIdentity).second val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public.tree, notaryNode.info.notaryIdentity).second
insertFakeTransactions(bobsFakeCash, bobNode) insertFakeTransactions(bobsFakeCash, bobNode)
val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
@ -433,7 +434,7 @@ class TwoPartyTradeProtocolTests {
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, notaryNode.info.notaryIdentity).second val bobsBadCash = fillUpForBuyer(bobError, bobKey.public.tree, notaryNode.info.notaryIdentity).second
val alicesFakePaper = fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey, val alicesFakePaper = fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey,
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second 1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
@ -480,7 +481,7 @@ class TwoPartyTradeProtocolTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer( private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
withError: Boolean, withError: Boolean,
owner: PublicKey = BOB_PUBKEY, owner: PublicKeyTree = BOB_PUBKEY,
notary: Party): Pair<Vault, List<WireTransaction>> { notary: Party): Pair<Vault, List<WireTransaction>> {
val issuer = DUMMY_CASH_ISSUER val issuer = DUMMY_CASH_ISSUER
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she // Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
@ -490,10 +491,10 @@ class TwoPartyTradeProtocolTests {
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
if (!withError) if (!withError)
command(DUMMY_CASH_ISSUER_KEY.public) { Cash.Commands.Issue() } command(DUMMY_CASH_ISSUER_KEY.public.tree) { Cash.Commands.Issue() }
else else
// Put a broken command on so at least a signature is created // Put a broken command on so at least a signature is created
command(DUMMY_CASH_ISSUER_KEY.public) { Cash.Commands.Move() } command(DUMMY_CASH_ISSUER_KEY.public.tree) { Cash.Commands.Move() }
timestamp(TEST_TX_TIME) timestamp(TEST_TX_TIME)
if (withError) { if (withError) {
this.fails() this.fails()
@ -524,7 +525,7 @@ class TwoPartyTradeProtocolTests {
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller( private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
withError: Boolean, withError: Boolean,
owner: PublicKey, owner: PublicKeyTree,
amount: Amount<Issued<Currency>>, amount: Amount<Issued<Currency>>,
attachmentID: SecureHash?, attachmentID: SecureHash?,
notary: Party): Pair<Vault, List<WireTransaction>> { notary: Party): Pair<Vault, List<WireTransaction>> {

View File

@ -1,12 +1,8 @@
package net.corda.node.services package net.corda.node.services
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import net.corda.core.contracts.ClientToServiceCommand
import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateAndRef
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.tree
import net.corda.core.messaging.Message import net.corda.core.messaging.Message
import net.corda.core.messaging.createMessage import net.corda.core.messaging.createMessage
import net.corda.core.node.services.DEFAULT_SESSION_ID import net.corda.core.node.services.DEFAULT_SESSION_ID
@ -147,7 +143,7 @@ class ArtemisMessagingTests {
private fun createMessagingClient(server: HostAndPort = hostAndPort): NodeMessagingClient { private fun createMessagingClient(server: HostAndPort = hostAndPort): NodeMessagingClient {
return databaseTransaction(database) { return databaseTransaction(database) {
NodeMessagingClient(config, server, identity.public, AffinityExecutor.ServiceAffinityExecutor("ArtemisMessagingTests", 1), database).apply { NodeMessagingClient(config, server, identity.public.tree, AffinityExecutor.ServiceAffinityExecutor("ArtemisMessagingTests", 1), database).apply {
configureWithDevSSLCertificate() configureWithDevSSLCertificate()
messagingClient = this messagingClient = this
} }

View File

@ -1,9 +1,9 @@
package net.corda.node.services package net.corda.node.services
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.tree
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.testing.expect import net.corda.testing.expect
import net.corda.testing.node.MockNetwork import net.corda.testing.node.MockNetwork
import org.junit.Before import org.junit.Before
@ -34,12 +34,12 @@ class InMemoryNetworkMapCacheTest {
val nodeB = network.createNode(null, -1, MockNetwork.DefaultFactory, true, "Node B", keyPair, ServiceInfo(NetworkMapService.type)) val nodeB = network.createNode(null, -1, MockNetwork.DefaultFactory, true, "Node B", keyPair, ServiceInfo(NetworkMapService.type))
// Node A currently knows only about itself, so this returns node A // Node A currently knows only about itself, so this returns node A
assertEquals(nodeA.netMapCache.getNodeByPublicKey(keyPair.public), nodeA.info) assertEquals(nodeA.netMapCache.getNodeByPublicKey(keyPair.public.tree), nodeA.info)
nodeA.netMapCache.addNode(nodeB.info) nodeA.netMapCache.addNode(nodeB.info)
// Now both nodes match, so it throws an error // Now both nodes match, so it throws an error
expect<IllegalStateException> { expect<IllegalStateException> {
nodeA.netMapCache.getNodeByPublicKey(keyPair.public) nodeA.netMapCache.getNodeByPublicKey(keyPair.public.tree)
} }
} }
} }

View File

@ -1,6 +1,8 @@
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.PublicKeyTree
import net.corda.core.crypto.tree
import net.corda.core.days import net.corda.core.days
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.recordTransactions import net.corda.core.node.recordTransactions
@ -109,7 +111,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
} }
class TestState(val protocolLogicRef: ProtocolLogicRef, val instant: Instant) : LinearState, SchedulableState { class TestState(val protocolLogicRef: ProtocolLogicRef, val instant: Instant) : LinearState, SchedulableState {
override val participants: List<PublicKey> override val participants: List<PublicKeyTree>
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
override val linearId = UniqueIdentifier() override val linearId = UniqueIdentifier()
@ -268,7 +270,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
val state = TestState(factory.create(TestProtocolLogic::class.java, increment), instant) val state = TestState(factory.create(TestProtocolLogic::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) addCommand(Command(), freshKey.public.tree)
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.DOLLARS
import net.corda.core.contracts.POUNDS import net.corda.core.contracts.POUNDS
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.`issued by` import net.corda.core.contracts.`issued by`
import net.corda.core.crypto.tree
import net.corda.core.node.services.TxWritableStorageService import net.corda.core.node.services.TxWritableStorageService
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -102,7 +103,7 @@ class NodeVaultServiceTest {
// Issue a txn to Send us some Money // Issue a txn to Send us some Money
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, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public.tree, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -116,7 +117,7 @@ class NodeVaultServiceTest {
// Issue more Money (GBP) // Issue more Money (GBP)
val anotherTX = TransactionType.General.Builder(null).apply { val anotherTX = TransactionType.General.Builder(null).apply {
Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY) Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), freshKey.public.tree, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()

View File

@ -3,6 +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.tree
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
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
@ -56,7 +57,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 val expectedMissingKey = MEGA_CORP_KEY.public.tree
val stx = run { val stx = run {
val inputState = issueState(clientNode) val inputState = issueState(clientNode)

View File

@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash
import net.corda.contracts.asset.DUMMY_CASH_ISSUER import net.corda.contracts.asset.DUMMY_CASH_ISSUER
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.tree
import net.corda.core.node.recordTransactions import net.corda.core.node.recordTransactions
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -72,7 +73,7 @@ class VaultWithCashTest {
val state = w.states.toList()[0].state.data as Cash.State val state = w.states.toList()[0].state.data as Cash.State
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, state.owner) assertEquals(services.key.public.tree, state.owner)
assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[2].state.data as Cash.State).amount) assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[2].state.data as Cash.State).amount)
assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[1].state.data as Cash.State).amount) assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[1].state.data as Cash.State).amount)
@ -85,7 +86,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, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public.tree, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -103,7 +104,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, DUMMY_NOTARY) Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_KEY.public.tree, DUMMY_NOTARY)
signWith(MEGA_CORP_KEY) signWith(MEGA_CORP_KEY)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -127,8 +128,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(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -148,7 +149,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(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
signWith(freshKey) signWith(freshKey)
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()
@ -160,7 +161,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(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public))) addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
addInputState(dummyIssue.tx.outRef<LinearState>(0)) addInputState(dummyIssue.tx.outRef<LinearState>(0))
signWith(DUMMY_NOTARY_KEY) signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction() }.toSignedTransaction()

View File

@ -6,10 +6,9 @@ import com.google.common.base.Throwables
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 com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import com.typesafe.config.Config
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.Party import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.protocols.ProtocolLogic import net.corda.core.protocols.ProtocolLogic
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -21,11 +20,9 @@ import net.corda.node.services.statemachine.StateMachineManager.Change
import net.corda.node.utilities.AddOrRemove.ADD import net.corda.node.utilities.AddOrRemove.ADD
import net.corda.testing.node.MockIdentityService import net.corda.testing.node.MockIdentityService
import net.corda.testing.node.MockServices import net.corda.testing.node.MockServices
import com.typesafe.config.Config
import rx.Subscriber import rx.Subscriber
import java.net.ServerSocket import java.net.ServerSocket
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@ -48,24 +45,24 @@ import kotlin.reflect.KClass
// A few dummy values for testing. // A few dummy values for testing.
val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() } val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() }
val MEGA_CORP_PUBKEY: PublicKey get() = MEGA_CORP_KEY.public val MEGA_CORP_PUBKEY: PublicKeyTree get() = MEGA_CORP_KEY.public.tree
val MINI_CORP_KEY: KeyPair by lazy { generateKeyPair() } val MINI_CORP_KEY: KeyPair by lazy { generateKeyPair() }
val MINI_CORP_PUBKEY: PublicKey get() = MINI_CORP_KEY.public val MINI_CORP_PUBKEY: PublicKeyTree get() = MINI_CORP_KEY.public.tree
val ORACLE_KEY: KeyPair by lazy { generateKeyPair() } val ORACLE_KEY: KeyPair by lazy { generateKeyPair() }
val ORACLE_PUBKEY: PublicKey get() = ORACLE_KEY.public val ORACLE_PUBKEY: PublicKeyTree get() = ORACLE_KEY.public.tree
val ALICE_KEY: KeyPair by lazy { generateKeyPair() } val ALICE_KEY: KeyPair by lazy { generateKeyPair() }
val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public val ALICE_PUBKEY: PublicKeyTree get() = ALICE_KEY.public.tree
val ALICE: Party get() = Party("Alice", ALICE_PUBKEY) val ALICE: Party get() = Party("Alice", ALICE_PUBKEY)
val BOB_KEY: KeyPair by lazy { generateKeyPair() } val BOB_KEY: KeyPair by lazy { generateKeyPair() }
val BOB_PUBKEY: PublicKey get() = BOB_KEY.public val BOB_PUBKEY: PublicKeyTree get() = BOB_KEY.public.tree
val BOB: Party get() = Party("Bob", BOB_PUBKEY) val BOB: Party get() = Party("Bob", BOB_PUBKEY)
val CHARLIE_KEY: KeyPair by lazy { generateKeyPair() } val CHARLIE_KEY: KeyPair by lazy { generateKeyPair() }
val CHARLIE_PUBKEY: PublicKey get() = CHARLIE_KEY.public val CHARLIE_PUBKEY: PublicKeyTree get() = CHARLIE_KEY.public.tree
val CHARLIE: Party get() = Party("Charlie", CHARLIE_PUBKEY) val CHARLIE: Party get() = Party("Charlie", CHARLIE_PUBKEY)
val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY) val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY)

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.*
import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.Clause
import net.corda.core.contracts.clauses.FilterOn 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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import java.security.PublicKey import java.security.PublicKey
@ -18,11 +19,11 @@ class DummyLinearContract: Contract {
class State( 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<PublicKey> = listOf(), override val participants: List<PublicKeyTree> = 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 {
return participants.any { ourKeys.contains(it) } return participants.any { it.containsAny(ourKeys) }
} }
} }
} }

View File

@ -9,7 +9,6 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.DUMMY_NOTARY_KEY import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.core.crypto.*
import java.io.InputStream import java.io.InputStream
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
@ -130,7 +129,7 @@ data class TestTransactionDSLInterpreter private constructor(
transactionBuilder.addAttachment(attachmentId) transactionBuilder.addAttachment(attachmentId)
} }
override fun _command(signers: List<PublicKey>, commandData: CommandData) { override fun _command(signers: List<PublicKeyTree>, commandData: CommandData) {
val command = Command(commandData, signers) val command = Command(commandData, signers)
transactionBuilder.addCommand(command) transactionBuilder.addCommand(command)
} }
@ -325,7 +324,7 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>)
(ALL_TEST_KEYS + extraKeys).forEach { (ALL_TEST_KEYS + extraKeys).forEach {
keyLookup[it.public] = it keyLookup[it.public] = it
} }
wtx.mustSign.forEach { wtx.mustSign.keys.forEach {
val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}") val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}")
signatures += key.signWithECDSA(wtx.id) signatures += key.signWithECDSA(wtx.id)
} }

View File

@ -2,11 +2,11 @@ package net.corda.testing
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.PublicKeyTree
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
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.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
@ -46,7 +46,7 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
* @param signers The signer public keys. * @param signers The signer public keys.
* @param commandData The contents of the command. * @param commandData The contents of the command.
*/ */
fun _command(signers: List<PublicKey>, commandData: CommandData) fun _command(signers: List<PublicKeyTree>, commandData: CommandData)
/** /**
* Adds a timestamp to the transaction. * Adds a timestamp to the transaction.
@ -99,12 +99,12 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
/** /**
* @see TransactionDSLInterpreter._command * @see TransactionDSLInterpreter._command
*/ */
fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) = fun command(vararg signers: PublicKeyTree, commandDataClosure: () -> CommandData) =
_command(listOf(*signers), commandDataClosure()) _command(listOf(*signers), commandDataClosure())
/** /**
* @see TransactionDSLInterpreter._command * @see TransactionDSLInterpreter._command
*/ */
fun command(signer: PublicKey, commandData: CommandData) = _command(listOf(signer), commandData) fun command(signer: PublicKeyTree, commandData: CommandData) = _command(listOf(signer), commandData)
/** /**
* Adds a timestamp command to the transaction. * Adds a timestamp command to the transaction.

View File

@ -2,10 +2,7 @@ package net.corda.testing.node
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.crypto.Party import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.sha256
import net.corda.core.messaging.MessagingService import net.corda.core.messaging.MessagingService
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
@ -63,18 +60,18 @@ open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub {
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException() override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
override val clock: Clock get() = throw UnsupportedOperationException() override val clock: Clock get() = throw UnsupportedOperationException()
override val schedulerService: SchedulerService get() = throw UnsupportedOperationException() override val schedulerService: SchedulerService get() = throw UnsupportedOperationException()
override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {} , Party("MegaCorp", key.public)) override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public.tree))
} }
@ThreadSafe @ThreadSafe
class MockIdentityService(val identities: List<Party>) : IdentityService, SingletonSerializeAsToken() { class MockIdentityService(val identities: List<Party>) : IdentityService, SingletonSerializeAsToken() {
private val keyToParties: Map<PublicKey, Party> private val keyToParties: Map<PublicKeyTree, Party>
get() = synchronized(identities) { identities.associateBy { it.owningKey } } get() = synchronized(identities) { identities.associateBy { it.owningKey } }
private val nameToParties: Map<String, Party> private val nameToParties: Map<String, Party>
get() = synchronized(identities) { identities.associateBy { it.name } } get() = synchronized(identities) { identities.associateBy { it.name } }
override fun registerIdentity(party: Party) { throw UnsupportedOperationException() } override fun registerIdentity(party: Party) { throw UnsupportedOperationException() }
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key] override fun partyFromKey(key: PublicKeyTree): Party? = keyToParties[key]
override fun partyFromName(name: String): Party? = nameToParties[name] override fun partyFromName(name: String): Party? = nameToParties[name]
} }

View File

@ -1,20 +1,5 @@
package net.corda.explorer.views package net.corda.explorer.views
import net.corda.client.fxutils.*
import net.corda.client.model.*
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.*
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.toStringShort
import net.corda.core.node.NodeInfo
import net.corda.core.protocols.StateMachineRunId
import net.corda.explorer.AmountDiff
import net.corda.explorer.formatters.AmountFormatter
import net.corda.explorer.identicon.identicon
import net.corda.explorer.identicon.identiconToolTip
import net.corda.explorer.model.ReportingCurrencyModel
import net.corda.explorer.sign
import net.corda.explorer.ui.setCustomCellFactory
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
import javafx.beans.value.ObservableValue import javafx.beans.value.ObservableValue
import javafx.collections.FXCollections import javafx.collections.FXCollections
@ -29,8 +14,23 @@ import javafx.scene.layout.BackgroundFill
import javafx.scene.layout.BorderPane import javafx.scene.layout.BorderPane
import javafx.scene.layout.CornerRadii import javafx.scene.layout.CornerRadii
import javafx.scene.paint.Color import javafx.scene.paint.Color
import net.corda.client.fxutils.*
import net.corda.client.model.*
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.*
import net.corda.core.crypto.PublicKeyTree
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.tree
import net.corda.core.node.NodeInfo
import net.corda.core.protocols.StateMachineRunId
import net.corda.explorer.AmountDiff
import net.corda.explorer.formatters.AmountFormatter
import net.corda.explorer.identicon.identicon
import net.corda.explorer.identicon.identiconToolTip
import net.corda.explorer.model.ReportingCurrencyModel
import net.corda.explorer.sign
import net.corda.explorer.ui.setCustomCellFactory
import tornadofx.* import tornadofx.*
import java.security.PublicKey
import java.util.* import java.util.*
class TransactionViewer : View() { class TransactionViewer : View() {
@ -136,7 +136,7 @@ class TransactionViewer : View() {
override val root: Parent by fxml() override val root: Parent by fxml()
private val inputs: ListView<StateNode> by fxid() private val inputs: ListView<StateNode> by fxid()
private val outputs: ListView<StateNode> by fxid() private val outputs: ListView<StateNode> by fxid()
private val signatures: ListView<PublicKey> by fxid() private val signatures: ListView<PublicKeyTree> by fxid()
private val inputPane: TitledPane by fxid() private val inputPane: TitledPane by fxid()
private val outputPane: TitledPane by fxid() private val outputPane: TitledPane by fxid()
private val signaturesPane: TitledPane by fxid() private val signaturesPane: TitledPane by fxid()
@ -148,7 +148,7 @@ class TransactionViewer : View() {
StateNode(PartiallyResolvedTransaction.InputResolution.Resolved(StateAndRef(transactionState, stateRef)).lift(), stateRef) StateNode(PartiallyResolvedTransaction.InputResolution.Resolved(StateAndRef(transactionState, stateRef)).lift(), stateRef)
} }
val signatureData = transaction.transaction.sigs.map { it.by } val signatureData = transaction.transaction.sigs.map { it.by.tree }
// Bind count to TitlePane // Bind count to TitlePane
inputPane.textProperty().bind(inputStates.lift().map { "Input (${it.count()})" }) inputPane.textProperty().bind(inputStates.lift().map { "Input (${it.count()})" })
outputPane.textProperty().bind(outputStates.lift().map { "Output (${it.count()})" }) outputPane.textProperty().bind(outputStates.lift().map { "Output (${it.count()})" })
@ -172,14 +172,14 @@ class TransactionViewer : View() {
} }
field("Issuer :") { field("Issuer :") {
label("${data.amount.token.issuer}") { label("${data.amount.token.issuer}") {
tooltip(data.amount.token.issuer.party.owningKey.toStringShort()) tooltip(data.amount.token.issuer.party.owningKey.toString())
} }
} }
field("Owner :") { field("Owner :") {
val owner = data.owner val owner = data.owner
val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(owner) val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(owner)
label(nodeInfo?.legalIdentity?.name ?: "???") { label(nodeInfo?.legalIdentity?.name ?: "???") {
tooltip(data.owner.toStringShort()) tooltip(data.owner.toString())
} }
} }
} }
@ -201,7 +201,7 @@ class TransactionViewer : View() {
signatures.apply { signatures.apply {
cellFormat { key -> cellFormat { key ->
val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(key) val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(key)
text = "${key.toStringShort()} (${nodeInfo?.legalIdentity?.name ?: "???"})" text = "$key (${nodeInfo?.legalIdentity?.name ?: "???"})"
} }
prefHeight = 185.0 prefHeight = 185.0
} }