From c33c55eb2087b482bdd4e7a6ce53652f2acb75b2 Mon Sep 17 00:00:00 2001 From: Andrius Dagys Date: Fri, 11 Nov 2016 18:14:50 +0000 Subject: [PATCH] Replace PublicKey with PublicKeyTree in Party. A single entity can now be identified by more than one key. --- .../net/corda/client/NodeMonitorModelTest.kt | 10 +-- .../client/model/NetworkIdentityModel.kt | 10 +-- .../net/corda/core/contracts/ContractsDSL.kt | 10 +-- .../net/corda/core/contracts/DummyContract.kt | 16 ++--- .../net/corda/core/contracts/DummyState.kt | 5 +- .../net/corda/core/contracts/FungibleAsset.kt | 9 +-- .../net/corda/core/contracts/Structures.kt | 16 ++--- .../corda/core/contracts/TransactionTypes.kt | 6 +- .../core/contracts/TransactionVerification.kt | 7 +-- .../net/corda/core/crypto/CryptoUtilities.kt | 9 +-- .../kotlin/net/corda/core/crypto/Party.kt | 24 +++++++- .../net/corda/core/crypto/PublicKeyTree.kt | 39 +++++++----- .../kotlin/net/corda/core/node/ServiceHub.kt | 9 ++- .../core/node/services/IdentityService.kt | 4 +- .../core/node/services/NetworkMapCache.kt | 4 +- .../net/corda/core/node/services/Services.kt | 13 +++- .../net/corda/core/serialization/Kryo.kt | 8 +-- .../net/corda/core/testing/Generators.kt | 12 ++-- .../core/transactions/BaseTransaction.kt | 4 +- .../core/transactions/LedgerTransaction.kt | 4 +- .../core/transactions/SignedTransaction.kt | 19 +++--- .../core/transactions/TransactionBuilder.kt | 16 ++--- .../core/transactions/WireTransaction.kt | 3 +- .../net/corda/core/utilities/ApiUtils.kt | 4 +- .../net/corda/core/utilities/TestConstants.kt | 9 +-- .../AbstractStateReplacementProtocol.kt | 8 +-- .../net/corda/protocols/FinalityProtocol.kt | 6 +- .../corda/protocols/NotaryChangeProtocol.kt | 4 +- .../net/corda/protocols/NotaryProtocol.kt | 9 +-- .../corda/protocols/TwoPartyDealProtocol.kt | 29 ++++----- .../net/corda/core/node/isolated.jar | Bin 7979 -> 7973 bytes .../contracts/TransactionEncumbranceTests.kt | 8 ++- .../contracts/TransactionGraphSearchTests.kt | 5 +- .../corda/core/contracts/TransactionTests.kt | 19 +++--- .../core/node/AttachmentClassLoaderTests.kt | 4 +- .../net/corda/core/node/VaultUpdateTests.kt | 4 +- .../TransactionSerializationTests.kt | 15 ++--- .../net/corda/contracts/AccountReceivable.kt | 11 ++-- .../corda/contracts/BillOfLadingAgreement.kt | 16 ++--- .../kotlin/net/corda/contracts/Invoice.kt | 5 +- .../net/corda/contracts/LCApplication.kt | 10 +-- .../main/kotlin/net/corda/contracts/LOC.kt | 4 +- .../contracts/universal/UniversalContract.kt | 6 +- .../net/corda/contracts/universal/Util.kt | 32 +++++----- .../corda/contracts/AnotherDummyContract.kt | 12 +--- .../contracts/ICommercialPaperState.java | 13 ++-- .../corda/contracts/JavaCommercialPaper.java | 58 ++++++++++-------- .../net/corda/contracts/CommercialPaper.kt | 23 +++---- .../corda/contracts/CommercialPaperLegacy.kt | 20 +++--- .../kotlin/net/corda/contracts/asset/Cash.kt | 31 +++++----- .../contracts/asset/CommodityContract.kt | 23 ++++--- .../net/corda/contracts/asset/Obligation.kt | 58 +++++++++--------- .../corda/contracts/asset/OnLedgerAsset.kt | 7 +-- .../clause/AbstractConserveAmount.kt | 8 +-- .../kotlin/net/corda/contracts/clause/Net.kt | 4 +- .../net/corda/contracts/testing/Generators.kt | 11 ++-- .../corda/contracts/testing/VaultFiller.kt | 9 ++- .../corda/protocols/TwoPartyTradeProtocol.kt | 28 ++++----- .../corda/contracts/CommercialPaperTests.kt | 7 ++- .../net/corda/contracts/asset/CashTests.kt | 10 +-- .../corda/contracts/asset/ObligationTests.kt | 14 ++--- .../corda/node/internal/CordaRPCOpsImpl.kt | 3 +- .../identity/InMemoryIdentityService.kt | 6 +- .../messaging/ArtemisMessagingComponent.kt | 12 ++-- .../services/messaging/NodeMessagingClient.kt | 4 +- .../node/services/messaging/RPCStructures.kt | 14 +++-- .../network/InMemoryNetworkMapCache.kt | 7 +-- .../services/network/NetworkMapService.kt | 6 +- .../node/services/vault/NodeVaultService.kt | 9 +-- .../corda/node/utilities/DatabaseSupport.kt | 20 ++++-- .../net/corda/node/CordaRPCOpsImplTest.kt | 10 +-- .../messaging/TwoPartyTradeProtocolTests.kt | 17 ++--- .../node/services/ArtemisMessagingTests.kt | 8 +-- .../services/InMemoryNetworkMapCacheTest.kt | 6 +- .../node/services/NodeSchedulerServiceTest.kt | 6 +- .../node/services/NodeVaultServiceTest.kt | 5 +- .../services/ValidatingNotaryServiceTests.kt | 3 +- .../corda/node/services/VaultWithCashTest.kt | 15 ++--- .../kotlin/net/corda/testing/CoreTestUtils.kt | 19 +++--- .../net/corda/testing/DummyLinearState.kt | 5 +- .../main/kotlin/net/corda/testing/TestDSL.kt | 5 +- .../testing/TransactionDSLInterpreter.kt | 8 +-- .../net/corda/testing/node/MockServices.kt | 11 ++-- .../corda/explorer/views/TransactionViewer.kt | 42 ++++++------- 84 files changed, 532 insertions(+), 510 deletions(-) diff --git a/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt b/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt index cb006bb435..342278205a 100644 --- a/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt +++ b/client/src/integration-test/kotlin/net/corda/client/NodeMonitorModelTest.kt @@ -174,8 +174,9 @@ class NodeMonitorModelTest { require(tx.tx.outputs.size == 1) val signaturePubKeys = tx.sigs.map { it.by }.toSet() // Only Alice signed - require(signaturePubKeys.size == 1) - require(signaturePubKeys.contains(aliceNode.legalIdentity.owningKey)) + val aliceKey = aliceNode.legalIdentity.owningKey + require(signaturePubKeys.size <= aliceKey.keys.size) + require(aliceKey.isFulfilledBy(signaturePubKeys)) issueTx = tx }, // MOVE @@ -184,9 +185,8 @@ class NodeMonitorModelTest { require(tx.tx.outputs.size == 1) val signaturePubKeys = tx.sigs.map { it.by }.toSet() // Alice and Notary signed - require(signaturePubKeys.size == 2) - require(signaturePubKeys.contains(aliceNode.legalIdentity.owningKey)) - require(signaturePubKeys.contains(notaryNode.notaryIdentity.owningKey)) + require(aliceNode.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys)) + require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys)) moveTx = tx } ) diff --git a/client/src/main/kotlin/net/corda/client/model/NetworkIdentityModel.kt b/client/src/main/kotlin/net/corda/client/model/NetworkIdentityModel.kt index 7ca7c2d96c..01ee2b5fcb 100644 --- a/client/src/main/kotlin/net/corda/client/model/NetworkIdentityModel.kt +++ b/client/src/main/kotlin/net/corda/client/model/NetworkIdentityModel.kt @@ -1,13 +1,13 @@ 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.map +import net.corda.core.crypto.PublicKeyTree import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache import net.corda.node.services.network.NetworkMapService -import javafx.collections.ObservableList -import kotlinx.support.jdk8.collections.removeIf -import java.security.PublicKey class NetworkIdentityModel { 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() } } - fun lookup(publicKey: PublicKey): NodeInfo? { - return parties.firstOrNull { it.legalIdentity.owningKey == publicKey } ?: notaries.firstOrNull { it.notaryIdentity.owningKey == publicKey } + fun lookup(publicKeyTree: PublicKeyTree): NodeInfo? { + return parties.firstOrNull { it.legalIdentity.owningKey == publicKeyTree } ?: notaries.firstOrNull { it.notaryIdentity.owningKey == publicKeyTree } } } diff --git a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt index fbb47d73dc..42cac3fcbb 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/ContractsDSL.kt @@ -2,7 +2,7 @@ package net.corda.core.contracts import net.corda.core.crypto.Party -import java.security.PublicKey +import net.corda.core.crypto.PublicKeyTree import java.util.* /** @@ -60,16 +60,16 @@ inline fun requireThat(body: Requirements.() -> R) = R.body() //// Authenticated commands /////////////////////////////////////////////////////////////////////////////////////////// /** Filters the command list by type, party and public key all at once. */ -inline fun Collection>.select(signer: PublicKey? = null, - party: Party? = null) = +inline fun Collection>.select(signer: PublicKeyTree? = null, + party: Party? = null) = filter { it.value is T }. filter { if (signer == null) true else signer in it.signers }. filter { if (party == null) true else party in it.signingParties }. map { AuthenticatedObject(it.signers, it.signingParties, it.value as T) } /** Filters the command list by type, parties and public keys all at once. */ -inline fun Collection>.select(signers: Collection?, - parties: Collection?) = +inline fun Collection>.select(signers: Collection?, + parties: Collection?) = filter { it.value is T }. filter { if (signers == null) true else it.signers.containsAll(signers)}. filter { if (parties == null) true else it.signingParties.containsAll(parties) }. diff --git a/core/src/main/kotlin/net/corda/core/contracts/DummyContract.kt b/core/src/main/kotlin/net/corda/core/contracts/DummyContract.kt index b03fe0f2f7..7c13cde8c5 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/DummyContract.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/DummyContract.kt @@ -1,9 +1,9 @@ package net.corda.core.contracts import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey // The dummy contract doesn't do anything useful. It exists for testing purposes. @@ -15,12 +15,12 @@ class DummyContract : Contract { 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 participants: List + override val participants: List 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. */ data class MultiOwnerState(override val magicNumber: Int = 0, - val owners: List) : ContractState, State { + val owners: List) : ContractState, State { override val contract = DUMMY_PROGRAM_ID - override val participants: List + override val participants: List get() = owners } @@ -54,8 +54,8 @@ class DummyContract : Contract { return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owner.party.owningKey)) } - fun move(prior: StateAndRef, newOwner: PublicKey) = move(listOf(prior), newOwner) - fun move(priors: List>, newOwner: PublicKey): TransactionBuilder { + fun move(prior: StateAndRef, newOwner: PublicKeyTree) = move(listOf(prior), newOwner) + fun move(priors: List>, newOwner: PublicKeyTree): TransactionBuilder { require(priors.size > 0) val priorState = priors[0].state.data val (cmd, state) = priorState.withNewOwner(newOwner) diff --git a/core/src/main/kotlin/net/corda/core/contracts/DummyState.kt b/core/src/main/kotlin/net/corda/core/contracts/DummyState.kt index 0eb8d4555f..18f57b2003 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/DummyState.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/DummyState.kt @@ -1,12 +1,11 @@ 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]. */ data class DummyState(val magicNumber: Int = 0) : ContractState { override val contract = DUMMY_PROGRAM_ID - override val participants: List + override val participants: List get() = emptyList() } diff --git a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt index 2b907a42fa..9313a2092a 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/FungibleAsset.kt @@ -1,6 +1,6 @@ package net.corda.core.contracts -import java.security.PublicKey +import net.corda.core.crypto.PublicKeyTree class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() { override fun toString() = "Insufficient balance, missing $amountMissing" @@ -26,10 +26,11 @@ interface FungibleAsset : OwnableState { * 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. */ - val exitKeys: Collection + val exitKeys: Collection /** There must be a MoveCommand signed by this key to claim the amount */ - override val owner: PublicKey - fun move(newAmount: Amount>, newOwner: PublicKey): FungibleAsset + override val owner: PublicKeyTree + + fun move(newAmount: Amount>, newOwner: PublicKeyTree): FungibleAsset // Just for grouping interface Commands : CommandData { diff --git a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt index 2c4c33b3d4..cf5a394082 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/Structures.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/Structures.kt @@ -2,8 +2,8 @@ package net.corda.core.contracts import net.corda.core.contracts.clauses.Clause import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.toStringShort import net.corda.core.node.services.ServiceType import net.corda.core.protocols.ProtocolLogicRef 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 * list should just contain the owner. */ - val participants: List + val participants: List /** * All contract states may be _encumbered_ by up to one other state. @@ -184,10 +184,10 @@ fun Amount>.withoutIssuer(): Amount = Amount(quantity, token.pr */ interface OwnableState : ContractState { /** 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 */ - fun withNewOwner(newOwner: PublicKey): Pair + fun withNewOwner(newOwner: PublicKeyTree): Pair } /** 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 */ -data class Command(val value: CommandData, val signers: List) { +data class Command(val value: CommandData, val signers: List) { init { 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 } - 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. */ @@ -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. */ data class AuthenticatedObject( - val signers: List, + val signers: List, /** If any public keys were recognised, the looked up institutions are available here */ val signingParties: List, val value: T diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt index 0dcbeddaeb..e2d987d217 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionTypes.kt @@ -1,9 +1,9 @@ package net.corda.core.contracts import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey /** Defines transaction build & validation logic for a specific transaction type */ sealed class TransactionType { @@ -25,7 +25,7 @@ sealed class TransactionType { } /** Check that the list of signers includes all the necessary keys */ - fun verifySigners(tx: LedgerTransaction): Set { + fun verifySigners(tx: LedgerTransaction): Set { val notaryKey = tx.inputs.map { it.state.notary.owningKey }.toSet() 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. * Note: the notary key is checked separately for all transactions and need not be included. */ - abstract fun getRequiredSigners(tx: LedgerTransaction): Set + abstract fun getRequiredSigners(tx: LedgerTransaction): Set /** Implement type specific transaction validation logic */ abstract fun verifyTransaction(tx: LedgerTransaction) diff --git a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt index 7fdaec7c4e..132131ba43 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/TransactionVerification.kt @@ -1,10 +1,9 @@ package net.corda.core.contracts import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.toStringShort import net.corda.core.transactions.LedgerTransaction -import java.security.PublicKey import java.util.* // 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) { class ContractRejection(tx: LedgerTransaction, val contract: Contract, cause: Throwable?) : TransactionVerificationException(tx, cause) class MoreThanOneNotary(tx: LedgerTransaction) : TransactionVerificationException(tx, null) - class SignersMissing(tx: LedgerTransaction, val missing: List) : TransactionVerificationException(tx, null) { - override fun toString() = "Signers missing: ${missing.map { it.toStringShort() }}" + class SignersMissing(tx: LedgerTransaction, val missing: List) : TransactionVerificationException(tx, null) { + override fun toString() = "Signers missing: ${missing.joinToString()}" } class InvalidNotaryChange(tx: LedgerTransaction) : TransactionVerificationException(tx, null) class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) { diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtilities.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtilities.kt index 61be31813e..8404618c65 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtilities.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtilities.kt @@ -31,7 +31,8 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) { 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 { @@ -42,6 +43,8 @@ object NullPublicKey : PublicKey, Comparable { override fun toString() = "NULL_KEY" } +val NullPublicKeyTree = NullPublicKey.tree + // TODO: Clean up this duplication between Null and Dummy public key class DummyPublicKey(val s: String) : PublicKey, Comparable { 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, party: Party) = signWithECDSA(bitsToSign.bits, party) fun KeyPair.signWithECDSA(bitsToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable { - check(public == party.owningKey) + check(public in party.owningKey.keys) val sig = signWithECDSA(bitsToSign) return DigitalSignature.LegallyIdentifiable(party, sig.bits) } @@ -99,8 +102,6 @@ fun PublicKey.toStringShort(): String { } ?: toString() } -fun Iterable.toStringsShort(): String = map { it.toStringShort() }.toString() - /** Creates a [PublicKeyTree] with a single leaf node containing the public key */ val PublicKey.tree: PublicKeyTree get() = PublicKeyTree.Leaf(this) diff --git a/core/src/main/kotlin/net/corda/core/crypto/Party.kt b/core/src/main/kotlin/net/corda/core/crypto/Party.kt index 4466aa1faf..d82be8fdd2 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/Party.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/Party.kt @@ -4,10 +4,28 @@ import net.corda.core.contracts.PartyAndReference import net.corda.core.serialization.OpaqueBytes 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 fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes) fun ref(vararg bytes: Byte) = ref(OpaqueBytes.of(*bytes)) -} +} \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/crypto/PublicKeyTree.kt b/core/src/main/kotlin/net/corda/core/crypto/PublicKeyTree.kt index 4fa720c21b..b3416b21a1 100644 --- a/core/src/main/kotlin/net/corda/core/crypto/PublicKeyTree.kt +++ b/core/src/main/kotlin/net/corda/core/crypto/PublicKeyTree.kt @@ -26,10 +26,10 @@ sealed class PublicKeyTree { fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key)) /** Returns all [PublicKey]s contained within the tree leaves */ - abstract fun getKeys(): Set + abstract val keys: Set /** Checks whether any of the given [keys] matches a leaf on the tree */ - fun containsAny(keys: Iterable) = getKeys().intersect(keys).isNotEmpty() + fun containsAny(otherKeys: Iterable) = keys.intersect(otherKeys).isNotEmpty() // TODO: implement a proper encoding/decoding mechanism fun toBase58String(): String = Base58.encode(this.serialize().bits) @@ -42,21 +42,17 @@ sealed class PublicKeyTree { class Leaf(val publicKey: PublicKey) : PublicKeyTree() { override fun isFulfilledBy(keys: Iterable) = publicKey in keys - override fun getKeys(): Set = setOf(publicKey) + override val keys: Set + 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 { - if (this === other) return true - if (other?.javaClass != javaClass) return false - - other as Leaf - - if (publicKey != other.publicKey) return false - - return true + return this === other || other is Leaf && other.publicKey == this.publicKey } override fun hashCode() = publicKey.hashCode() + + override fun toString() = publicKey.toStringShort() } /** @@ -78,7 +74,8 @@ sealed class PublicKeyTree { return totalWeight >= threshold } - override fun getKeys(): Set = children.flatMap { it.getKeys() }.toSet() + override val keys: Set + get() = children.flatMap { it.keys }.toSet() // Auto-generated. TODO: remove once data class inheritance is enabled override fun equals(other: Any?): Boolean { @@ -100,6 +97,8 @@ sealed class PublicKeyTree { result = 31 * result + children.hashCode() return result } + + override fun toString() = "(${children.joinToString()})" } /** A helper class for building a [PublicKeyTree.Node]. */ @@ -119,8 +118,7 @@ sealed class PublicKeyTree { return this } - fun addLeaves(publicKeys: List): Builder = addLeaves(*publicKeys.toTypedArray()) - fun addLeaves(vararg publicKeys: PublicKey) = addKeys(*publicKeys.map { it.tree }.toTypedArray()) + fun addKeys(publicKeys: List): Builder = addKeys(*publicKeys.toTypedArray()) /** * 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()) } } + + /** + * 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 */ -fun Iterable.getKeys() = flatMap { it.getKeys() }.toSet() +val Iterable.keys: Set + get() = flatMap { it.keys }.toSet() \ No newline at end of file diff --git a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt index 8cbbbffbc2..8618432e82 100644 --- a/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt +++ b/core/src/main/kotlin/net/corda/core/node/ServiceHub.kt @@ -1,13 +1,13 @@ package net.corda.core.node import com.google.common.util.concurrent.ListenableFuture -import net.corda.core.transactions.SignedTransaction import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionResolutionException import net.corda.core.contracts.TransactionState import net.corda.core.messaging.MessagingService import net.corda.core.node.services.* import net.corda.core.protocols.ProtocolLogic +import net.corda.core.transactions.SignedTransaction import java.security.KeyPair import java.time.Clock @@ -59,8 +59,11 @@ interface ServiceHub { * Helper property to shorten code for fetching the Node's KeyPair associated with the * public legalIdentity Party from the key management service. * 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 @@ -69,7 +72,7 @@ interface ServiceHub { * an IllegalArgumentException. * 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) } /** diff --git a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt index 65073bf48d..7f6dbefb7f 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/IdentityService.kt @@ -1,7 +1,7 @@ package net.corda.core.node.services 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 @@ -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, // but for now this is not supported. - fun partyFromKey(key: PublicKey): Party? + fun partyFromKey(key: PublicKeyTree): Party? fun partyFromName(name: String): Party? } diff --git a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt index 5964039714..54931d48f3 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/NetworkMapCache.kt @@ -4,12 +4,12 @@ import com.google.common.annotations.VisibleForTesting import com.google.common.util.concurrent.ListenableFuture import net.corda.core.contracts.Contract import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.messaging.MessagingService import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.node.NodeInfo import org.slf4j.LoggerFactory 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 @@ -74,7 +74,7 @@ interface NetworkMapCache { /** * 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 diff --git a/core/src/main/kotlin/net/corda/core/node/services/Services.kt b/core/src/main/kotlin/net/corda/core/node/services/Services.kt index 17c16b90e0..7deff0d924 100644 --- a/core/src/main/kotlin/net/corda/core/node/services/Services.kt +++ b/core/src/main/kotlin/net/corda/core/node/services/Services.kt @@ -4,7 +4,9 @@ import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.toStringShort import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import rx.Observable @@ -184,8 +186,8 @@ interface VaultService { @Throws(InsufficientBalanceException::class) fun generateSpend(tx: TransactionBuilder, amount: Amount, - to: PublicKey, - onlyFromParties: Set? = null): Pair> + to: PublicKeyTree, + onlyFromParties: Set? = null): Pair> } inline fun VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java) @@ -201,14 +203,19 @@ inline fun 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 * interface if/when one is developed. */ + interface KeyManagementService { /** Returns a snapshot of the current pubkey->privkey mapping. */ val keys: Map - 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)) + /** Returns the first [KeyPair] matching any of the [publicKeys] */ + fun toKeyPair(publicKeys: Iterable) = publicKeys.first { keys.contains(it) }.let { toKeyPair(it) } + /** Generates a new random key and adds it to the exposed map. */ fun freshKey(): KeyPair } diff --git a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt index 98f49c8436..44cfaa17bb 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/Kryo.kt @@ -10,6 +10,8 @@ import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.serializers.JavaSerializer 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.crypto.* 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.utilities.NonEmptySet 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.EdDSAPublicKey import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec @@ -32,7 +31,6 @@ import java.io.ObjectOutputStream import java.lang.reflect.InvocationTargetException import java.nio.file.Files import java.nio.file.Path -import java.security.PublicKey import java.time.Instant import java.util.* import javax.annotation.concurrent.ThreadSafe @@ -268,7 +266,7 @@ object WireTransactionSerializer : Serializer() { val outputs = kryo.readClassAndObject(input) as List> val commands = kryo.readClassAndObject(input) as List val notary = kryo.readClassAndObject(input) as Party? - val signers = kryo.readClassAndObject(input) as List + val signers = kryo.readClassAndObject(input) as List val transactionType = kryo.readClassAndObject(input) as TransactionType val timestamp = kryo.readClassAndObject(input) as Timestamp? diff --git a/core/src/main/kotlin/net/corda/core/testing/Generators.kt b/core/src/main/kotlin/net/corda/core/testing/Generators.kt index b90b2fb433..0a39520463 100644 --- a/core/src/main/kotlin/net/corda/core/testing/Generators.kt +++ b/core/src/main/kotlin/net/corda/core/testing/Generators.kt @@ -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.random.SourceOfRandomness import net.corda.core.contracts.* -import net.corda.core.crypto.Party -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.entropyToKeyPair +import net.corda.core.crypto.* import net.corda.core.serialization.OpaqueBytes import java.security.PrivateKey import java.security.PublicKey @@ -41,9 +39,15 @@ class PublicKeyGenerator: Generator(PublicKey::class.java) { } } +class PublicKeyTreeGenerator : Generator(PublicKeyTree::class.java) { + override fun generate(random: SourceOfRandomness, status: GenerationStatus): PublicKeyTree { + return entropyToKeyPair(random.nextBigInteger(32)).public.tree + } +} + class PartyGenerator: Generator(Party::class.java) { 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)) } } diff --git a/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt index b07601a569..ba26ac127a 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/BaseTransaction.kt @@ -2,7 +2,7 @@ package net.corda.core.transactions import net.corda.core.contracts.* import net.corda.core.crypto.Party -import java.security.PublicKey +import net.corda.core.crypto.PublicKeyTree import java.util.* /** @@ -25,7 +25,7 @@ abstract class BaseTransaction( * transaction until the transaction is verified by using [LedgerTransaction.verify]. It includes the * notary key, if the notary field is set. */ - val mustSign: List, + val mustSign: List, /** * Pointer to a class that defines the behaviour of this transaction: either normal, or "notary changing". */ diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index c466ae21ad..21cb6b2477 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -2,8 +2,8 @@ package net.corda.core.transactions import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree 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: @@ -27,7 +27,7 @@ class LedgerTransaction( /** The hash of the original serialised WireTransaction. */ override val id: SecureHash, notary: Party?, - signers: List, + signers: List, timestamp: Timestamp?, type: TransactionType ) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) { diff --git a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt index 839b0b057e..cf1dae1584 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/SignedTransaction.kt @@ -3,12 +3,11 @@ package net.corda.core.transactions import net.corda.core.contracts.NamedByHash import net.corda.core.contracts.TransactionResolutionException import net.corda.core.crypto.DigitalSignature +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.toStringsShort import net.corda.core.node.ServiceHub import net.corda.core.serialization.SerializedBytes import java.io.FileNotFoundException -import java.security.PublicKey import java.security.SignatureException import java.util.* @@ -34,9 +33,9 @@ data class SignedTransaction(val txBits: SerializedBytes, /** Lazily calculated access to the deserialised/hashed transaction data. */ val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) } - class SignaturesMissingException(val missing: Set, val descriptions: List, override val id: SecureHash) : NamedByHash, SignatureException() { + class SignaturesMissingException(val missing: Set, val descriptions: List, override val id: SecureHash) : NamedByHash, SignatureException() { 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, * @throws SignaturesMissingException if any signatures should have been present but were not. */ @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. checkSignaturesAreValid() @@ -83,19 +82,17 @@ data class SignedTransaction(val txBits: SerializedBytes, } } - private fun getMissingSignatures(): Set { - val requiredKeys = tx.mustSign.toSet() + private fun getMissingSignatures(): Set { val sigKeys = sigs.map { it.by }.toSet() - - if (sigKeys.containsAll(requiredKeys)) return emptySet() - return requiredKeys - sigKeys + val missing = tx.mustSign.filter { !it.isFulfilledBy(sigKeys) }.toSet() + return missing } /** * Get a human readable description of where signatures are required from, and are missing, to assist in debugging * the underlying cause. */ - private fun getMissingKeyDescriptions(missing: Set): ArrayList { + private fun getMissingKeyDescriptions(missing: Set): ArrayList { // TODO: We need a much better way of structuring this data val missingElements = ArrayList() this.tx.commands.forEach { command -> diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 61d5784d6a..aac493468c 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -3,11 +3,7 @@ package net.corda.core.transactions import net.corda.core.contracts.* import net.corda.core.crypto.* 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.PublicKey import java.time.Duration import java.time.Instant import java.util.* @@ -35,7 +31,7 @@ open class TransactionBuilder( protected val attachments: MutableList = arrayListOf(), protected val outputs: MutableList> = arrayListOf(), protected val commands: MutableList = arrayListOf(), - protected val signers: MutableSet = mutableSetOf(), + protected val signers: MutableSet = mutableSetOf(), protected var timestamp: Timestamp? = null) { val time: Timestamp? get() = timestamp @@ -124,7 +120,7 @@ open class TransactionBuilder( * @throws IllegalArgumentException if the signature key doesn't appear in any command. */ 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) } @@ -140,9 +136,9 @@ open class TransactionBuilder( fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction { if (checkSufficientSignatures) { val gotKeys = currentSigs.map { it.by }.toSet() - val missing: Set = signers - gotKeys + val missing: Set = signers.filter { !it.isFulfilledBy(gotKeys) }.toSet() 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() return SignedTransaction(wtx.serialize(), ArrayList(currentSigs), wtx.id) @@ -182,8 +178,8 @@ open class TransactionBuilder( commands.add(arg) } - fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys))) - fun addCommand(data: CommandData, keys: List) = addCommand(Command(data, keys)) + fun addCommand(data: CommandData, vararg keys: PublicKeyTree) = addCommand(Command(data, listOf(*keys))) + fun addCommand(data: CommandData, keys: List) = addCommand(Command(data, keys)) // Accessors that yield immutable snapshots. fun inputStates(): List = ArrayList(inputs) diff --git a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt index c3098eeece..7f1be20a3a 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/WireTransaction.kt @@ -3,6 +3,7 @@ package net.corda.core.transactions import com.esotericsoftware.kryo.Kryo import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.indexOfOrThrow 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. */ val commands: List, notary: Party?, - signers: List, + signers: List, type: TransactionType, timestamp: Timestamp? ) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) { diff --git a/core/src/main/kotlin/net/corda/core/utilities/ApiUtils.kt b/core/src/main/kotlin/net/corda/core/utilities/ApiUtils.kt index 77bbb8fa8a..753852da02 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/ApiUtils.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/ApiUtils.kt @@ -1,7 +1,7 @@ package net.corda.core.utilities 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 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 { return try { - val partyKey = parsePublicKeyBase58(partyKeyStr) + val partyKey = PublicKeyTree.parseFromBase58(partyKeyStr) val party = services.identityService.partyFromKey(partyKey) if(party == null) notFound("Unknown party") else found(party) } catch (e: IllegalArgumentException) { diff --git a/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt b/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt index fa04f6d550..b8d6c6593f 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/TestConstants.kt @@ -2,20 +2,15 @@ package net.corda.core.utilities 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.security.KeyPair -import java.security.PublicKey import java.time.Instant // 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 DUMMY_PUBKEY_1: PublicKey get() = DummyPublicKey("x1") -val DUMMY_PUBKEY_2: PublicKey get() = DummyPublicKey("x2") +val DUMMY_PUBKEY_1: PublicKeyTree get() = DummyPublicKey("x1").tree +val DUMMY_PUBKEY_2: PublicKeyTree get() = DummyPublicKey("x2").tree val DUMMY_KEY_1: KeyPair by lazy { generateKeyPair() } val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() } diff --git a/core/src/main/kotlin/net/corda/protocols/AbstractStateReplacementProtocol.kt b/core/src/main/kotlin/net/corda/protocols/AbstractStateReplacementProtocol.kt index 858f647cf2..c6ee334510 100644 --- a/core/src/main/kotlin/net/corda/protocols/AbstractStateReplacementProtocol.kt +++ b/core/src/main/kotlin/net/corda/protocols/AbstractStateReplacementProtocol.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.signWithECDSA import net.corda.core.node.recordTransactions 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.protocols.AbstractStateReplacementProtocol.Acceptor 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. @@ -67,10 +67,10 @@ abstract class AbstractStateReplacementProtocol { } abstract protected fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal - abstract protected fun assembleTx(): Pair> + abstract protected fun assembleTx(): Pair> @Suspendable - private fun collectSignatures(participants: List, stx: SignedTransaction): List { + private fun collectSignatures(participants: List, stx: SignedTransaction): List { val parties = participants.map { val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(it) ?: throw IllegalStateException("Participant $it to state $originalState not found on the network") @@ -95,7 +95,7 @@ abstract class AbstractStateReplacementProtocol { val participantSignature = response.unwrap { if (it.sig == null) throw StateReplacementException(it.error!!) 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 } diff --git a/core/src/main/kotlin/net/corda/protocols/FinalityProtocol.kt b/core/src/main/kotlin/net/corda/protocols/FinalityProtocol.kt index fc3d4cc63f..673e839392 100644 --- a/core/src/main/kotlin/net/corda/protocols/FinalityProtocol.kt +++ b/core/src/main/kotlin/net/corda/protocols/FinalityProtocol.kt @@ -50,5 +50,9 @@ class FinalityProtocol(val transaction: SignedTransaction, } 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) + } } diff --git a/core/src/main/kotlin/net/corda/protocols/NotaryChangeProtocol.kt b/core/src/main/kotlin/net/corda/protocols/NotaryChangeProtocol.kt index f586e150da..9e4a996ac7 100644 --- a/core/src/main/kotlin/net/corda/protocols/NotaryChangeProtocol.kt +++ b/core/src/main/kotlin/net/corda/protocols/NotaryChangeProtocol.kt @@ -6,12 +6,12 @@ import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateRef import net.corda.core.contracts.TransactionType import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.UntrustworthyData import net.corda.protocols.NotaryChangeProtocol.Acceptor 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 @@ -36,7 +36,7 @@ object NotaryChangeProtocol: AbstractStateReplacementProtocol() { override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal = Proposal(stateRef, modification, stx) - override fun assembleTx(): Pair> { + override fun assembleTx(): Pair> { val state = originalState.state val newState = state.withNotary(modification) val participants = state.data.participants diff --git a/core/src/main/kotlin/net/corda/protocols/NotaryProtocol.kt b/core/src/main/kotlin/net/corda/protocols/NotaryProtocol.kt index 827773ff32..fa7ef94067 100644 --- a/core/src/main/kotlin/net/corda/protocols/NotaryProtocol.kt +++ b/core/src/main/kotlin/net/corda/protocols/NotaryProtocol.kt @@ -1,21 +1,16 @@ package net.corda.protocols import co.paralleluniverse.fibers.Suspendable -import net.corda.core.crypto.DigitalSignature -import net.corda.core.crypto.Party -import net.corda.core.crypto.SignedData -import net.corda.core.crypto.signWithECDSA +import net.corda.core.crypto.* import net.corda.core.node.services.TimestampChecker import net.corda.core.node.services.UniquenessException import net.corda.core.node.services.UniquenessProvider import net.corda.core.protocols.ProtocolLogic -import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.serialize import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.UntrustworthyData -import java.security.PublicKey object NotaryProtocol { @@ -177,5 +172,5 @@ sealed class NotaryError { class TransactionInvalid : NotaryError() - class SignaturesMissing(val missingSigners: Set) : NotaryError() + class SignaturesMissing(val missingSigners: Set) : NotaryError() } diff --git a/core/src/main/kotlin/net/corda/protocols/TwoPartyDealProtocol.kt b/core/src/main/kotlin/net/corda/protocols/TwoPartyDealProtocol.kt index 53da60933d..13d392e9a0 100644 --- a/core/src/main/kotlin/net/corda/protocols/TwoPartyDealProtocol.kt +++ b/core/src/main/kotlin/net/corda/protocols/TwoPartyDealProtocol.kt @@ -1,11 +1,10 @@ package net.corda.protocols import co.paralleluniverse.fibers.Suspendable -import net.corda.core.TransientProperty -import net.corda.core.contracts.* -import net.corda.core.crypto.DigitalSignature -import net.corda.core.crypto.Party -import net.corda.core.crypto.signWithECDSA +import net.corda.core.contracts.ContractState +import net.corda.core.contracts.DealState +import net.corda.core.contracts.StateRef +import net.corda.core.crypto.* import net.corda.core.node.NodeInfo import net.corda.core.node.recordTransactions 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.UntrustworthyData import net.corda.core.utilities.trace -import java.math.BigDecimal import java.security.KeyPair -import java.security.PublicKey /** * 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. - data class Handshake(val payload: T, val publicKey: PublicKey) + data class Handshake(val payload: T, val publicKey: PublicKeyTree) class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.LegallyIdentifiable) @@ -90,7 +87,7 @@ object TwoPartyDealProtocol { progressTracker.currentStep = AWAITING_PROPOSAL // 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(otherParty, hello) return maybeSTX @@ -104,7 +101,7 @@ object TwoPartyDealProtocol { progressTracker.nextStep() // 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}" } checkDependencies(stx) @@ -251,18 +248,18 @@ object TwoPartyDealProtocol { return sendAndReceive(otherParty, stx).unwrap { it } } - private fun signWithOurKeys(signingPubKeys: List, ptx: TransactionBuilder): SignedTransaction { + private fun signWithOurKeys(signingPubKeys: List, ptx: TransactionBuilder): SignedTransaction { // Now sign the transaction with whatever keys we need to move the cash. - for (k in signingPubKeys) { - val priv = serviceHub.keyManagementService.toPrivate(k) - ptx.signWith(KeyPair(k, priv)) + for (publicKey in signingPubKeys.keys) { + val privateKey = serviceHub.keyManagementService.toPrivate(publicKey) + ptx.signWith(KeyPair(publicKey, privateKey)) } return ptx.toSignedTransaction(checkSufficientSignatures = false) } @Suspendable protected abstract fun validateHandshake(handshake: Handshake): Handshake - @Suspendable protected abstract fun assembleSharedTX(handshake: Handshake): Pair> + @Suspendable protected abstract fun assembleSharedTX(handshake: Handshake): Pair> } @@ -295,7 +292,7 @@ object TwoPartyDealProtocol { return handshake.copy(payload = autoOffer.copy(dealBeingOffered = deal)) } - override fun assembleSharedTX(handshake: Handshake): Pair> { + override fun assembleSharedTX(handshake: Handshake): Pair> { val deal = handshake.payload.dealBeingOffered val ptx = deal.generateAgreement(handshake.payload.notary) diff --git a/core/src/main/resources/net/corda/core/node/isolated.jar b/core/src/main/resources/net/corda/core/node/isolated.jar index 0e80a251edba3cf2f18179e773b62696ef8a7f61..62cce72de99a1909957d2b3399eebb62d56d0155 100644 GIT binary patch delta 4412 zcmV-C5yS4QKBYdO1Ahojd6h>FP@#r|8&pWa6;dSw5>&b5fHv_;UBzA_?*=Ki{uWML zIU#XCRge5A#5fPp1D8^HXLjb>Z)QBR`|J0Qp8$Gj6Z%HE&N$0s$=bMFj-Bmjnp>kMR8e z3;QS&D*M`K_mbc}dOWDXM>RkRYlM!%MeFpWC!P|EcvU|8QW16U>}#b|MDz%I(M4v*_4hB) z8Hca0m>*NA4Sx@E(JY~R;j}((5~{1*gsp#(_9l)mCCp+j(TK9KOa?O7a+Ii}LJ>A1 zZPeRoI#T%(2|i@ge5_vU!l@k^qw+A3)+(;C{eEhkPSrqL&E&q}lG5qSSac!y32;Y? zD=FRt>$~hdoBKd{_d9}bYzXYIt}Q@ZVGFp&67UJoWKO&Z5BKNHJ9xl*gDt_SxB08! z7H(J8@R0G4x$B(c>JZ)`HWmx@mAAM z8PKo$0g}Uf@gxM`v1UXL`|})ai8kp50Bv!1y_n?7rum_dMr)-mkNN|M&bi z04H#t;k2cTOx|`18a*xHXnDai2DeR3=!HzbWs5o88FWjfrGa3%eSbpGS_F(CF|RFZ z8B?>0nX%bFDclD-Z!f9fMj~WH>qU-lp_292~KoVrE_!vyNt1Jfm5p zrwPL*dDIr8u4$6imZB~OswbUFcYM%Uhidk1%B?q!7`DWqU@OD^x2i|b7)JzZlr}Um zgvFe}8P2}HeSTQT%6~@UdR=pbkvGbkB?v7^c6*U>g<)n!3@*u}7HnY}7Qve)Mb2~B zz$!*sS|%3Gg`;{e)Y2;&e%|Gf!I3y(0g(#id!V}FxZhsI|HZ{%{*GuOw* zu8jBRX0n5EbjVPo(MboSv)Pe=^Za5dV^~6WEX~Xe>UT8Pl3`^F#b)-!(SuK8IEB*;?+nBc%iE>0ZRwWip%?mg>mS9UN1`_g3lPnCR|!bJ))vt=GHn!6T&g9 zVvv{vF{4S=Rh5L&qI8E|{#dUh!q=jSgdAP{ z^I_=PwnP)jq*_&?X*S-Rs3W=l=F9KHv`MAoT7SZ7B%zKsRgr?pXd*V=lGxIsCc{ak zduw<6%v7+eo>m(YjW2(xPf7+Sm}C<#zt-^&ZK!8wsVwZwb$8Y@^27Slq@&CH-dZVx znG$sl?dh;>b#_(^x910_yW~*>$qzPqoil8MS~mE_xfM;LTV#^Y?C8470;CB z7=N-ePRa*eXCsEC`ij}ZD+jo-3EOq@`ej27 z;#temodHwhTqg^62lh{Gu%5c8LRT1NCcTsLDks^Z6sxCql-@~s3(~uR#`I-CS~iU` zG6m^f*ZC`w%Oof$(=+Z*VjG(2eK&vs8h_VACi)3TYD%C~0Hk)VpkWP(WiPv5?UcuE z#}0%%?c;%V_!(MjPYrK4wF!==!(SuRguU1ooW7pM{>N{nA&sFcHQ1abZAAihuHtZK z>r?FSe1@*4NIt0o?eG->ASp>_`ut+vS_9eCn2@kX1K4ij8kQ@aZQ*Ypp)SdH+#X9{tSiLD!QM*?-xDvy<8i*XE?K2=+4$~t~l?cN8nu5 zc?WBq$2M_ZlWg$8$vwnOa*n=$GA&6hS2y{NidG#J4IH&TZL&Cz3jx%bz)Aa#{e`VB zaB(_$X%$1i`z}+ZL4B<&xZ)omJ%1H}(lAVq*HVGWCLORz)0#BA?FELXS22dO2|Bt*Q^2YJDJ=Z{y2g`6_|RhMn&0`UwZR{($lbd%D)36d8Px{R4$QIN^GsDq+#f9qOG}fJtAq_h?+A@laRf^RI6s zl*Nqy!#9jbp>ZRa$u}UbPcC9;-eRU3NiJ_%qgPR9lZIhR zkQlu4+|(W~GF)tm?Yh92spx`6nxXd_W*(=zd7R;N%rK|q8E#FQilz%$(J3m$wiNQP zVGUJr6%vjzRI1ZloxQ7=N}7{J zRODb7&NaohGSj_56SrZe$g2Q?IOazwLJS^jS`!QxVuwxzx!rMSN*F3qd`d~>6{Gwy zH@U8IhEv9nFJCA5Dr^c+6U+;kD{Gr1FSJA=C^yLhJ`9k@VsvESl1 z6-$wDiJ|4-$(L}2!AI5Td|{~HwfM4p1aJ*qeq05m`NSTE5^gXA2zO*br*6H&7wx{@ z6+1JT(o`}UGdZUPXLz8~cHW>mM@7RVr;)H4 zETcg;^j7DSMbqq-YI9a>D=dH3mZzs}Cwkf(7ks$KP~Nojxo`$=_%Vk23`eO1hc?ne zT@?-BO-$Ik#{-4~hs<#HV3rad`QfoUcH4HbbsLv3Np(ndJg4Zy+D;kYIwf4NrgBSy z7emEk%EYu~&516V4rYh3q|I0xo!LH2@30~4jw)e>Hd`^RbY8m5aASWbg7^0^KF~wc z$-xxTe$0X*I~L2;$`H4dq}{S~T}aQn=?+C#A=jic0+sD7y(K#s{$5+^_q(cG>wjNf zx*fT;R1iU5{w;6hXkF`Cch%Zbr>mXO-__-+tm-X#`K{L#bPqNJeKoH3;!bbSPx1)K z0Lc=PM@g0rR15}#!JL2Q7|D>MIZmT8(v*{|7^oO63wmf&bpPeo9>TS~sqy%{LFwgp z-fBCZPWuM6Z}|`{}agWk;y<_P5|3{dFCA!@0 z*toM69U7w<-O|!@SDDv@Hksl*>o-X{-1{h8hEU8U1gSODRHdvaQZRj&F004q+FywF_SO zh{TY60VDY#Rjrrg5t2nDi{U4mkLazNq>H2!36=bc<3G~Sg^%f3kyWD%<#x@}C_p-f zPjXOR)ZkNF&vJjbwEg{-avnC!L3?y_2DM^ z!}u&az9-@Bt*M|389nV~d*ODXDQj7Q(()6gLfSHnHK@O!c0JqK%#>}&jNfsfgq*G; zgwGwz7x;gIWQay#`ah?He?an?w-{fN{sm#o5wGt|z#WB|{Xyj4Eczj1tL7)b1QzgQ z0yP5V0*eCg3Umro3RDSH3tL|&@SZ@uKulmr;Hf~Pz=*(IfkAzFpAW$LjOyIe| z`~Lw@O9u$i$HB>51^@u?4gdg9O9KQH00;;O005J`1`m^36jOh@iR&aZ;nl<~ua-r+ z!K8>46;hP&h{yw_rBaneG>%8Xi64?ZiNvno!ipu21rkW8>XMH_+zF`=DlE$A+H>!j zbLZSS-+z4h24EL=86HGJI-@x8IQ=7;@R3X%KaB$}h3C|xSiTlXa~6hg8q4DsG7H8~ zobWk!0v?T>o;!aLBSLkCa=qI-c>bh6Y`*Grj)pJ$y=VP;XV_|zQv1SEj~KSwaWZx$ zLb?g}qtxLM@woJ3%Db`b&Vqng8)G5=t^BaM^B>xItRS-r4HkpBHwyg7mya3r>dqlU zsXdJ)K~6_V?s3U!2rZoJWGi{2xr+=8Y^oBCAg{(yJvo22Ec&#vW@)CrRVtnw+uD70 z`Zi+{ZX2dn)KA}K48`AND4b0=4C(O9?ucBvdWqBXaK-J$IH|AooX~i98CFtFCS8Rx9@v_R(kD z_=MbtbIlsI7IHU<4m1e8MB}Utptn%`6)2rmmnm?ig{!zW084Ql>jSK$Dl#d|6ssx9 zKLJon2MA@i!s_V(00313002-+0|XQR2nYxO005J{1`e~g7BB?_Z>jIDlj|Gt2__wu z0z(D>0D+U`9BT>D$HB>51^@u?lUW^Q9A&q{>gfRh096A30672&00000000000000w z6qA7*Gn0=L3X?e<7n3d?8UdG+NghQ3#iKC3>U1AiV(flwYfK!q9-ZcrfwS4fo%NKoaH1KPwZbrpM!yc?w4`dc`0 z<%GlmRXy^f5aT>V4_r#+o!OahznSsO?yuiJegf#BP3RltI^!&lC2QkyId-$9FYXd{PfzNBXo|m570uI=o_I5z!;;MHiVJ*WbTH zXB@u1Vt!1eHh(0d`^RarZ3#WEyjLO4ATC2Fm_WP-EI#mO0HIw^>OG>9RW6_1+C%_#o zuB3Pqtnae-Z0-Z)-R}s#u_3U-y0!pug)QJ7OTZ^Ui%#(tJlvl%TX?{GgDt_SxB08! zHttl`@R0G4x$B(c>JZ)`HWmx@mAA!qs%ab0&8zO&G=8pzVk`!#(Gf%S%Jybbr^Rpt%?rLuy`I z)N+<)mvZB?^M)>Bh%oG33+WnbaH|j@LIZ&1;gzR z9PE^-JSbZSWGfeN9kz8M>T0%bFl?#D*FOn)0xr>VhOXCXlZ2_wS~6ir-tN3~g^uRS z6^qC4F~h0sn}6jPsA4_;lIX)H2^_^ShPMV{Na#+v;@E~Q`Y4&YQ(0mdpqzeyVv`c3 zs~FH@_%{3ASd`-WlGOhpp6z>qcGc!yrQ*1>X1rp!ej11G=tf00V)&F{eA1(djILQ0 z&)hb}TqdNH(cF?(rik$jZI-FwWK26F&wECYfguiLZhv~VfBH^lWJFX9N^PxSG$~xu zE``DzO5hAg;{6zq!aPsKR5*bJL(5B&4A(Jitsgt8v#xw|_j%;&p+krusTVbM=rH451Y@p%HDRycnSik8Uhw)^C9}S)g8#H2ie+?%ss5 zP4WM$cN0oVO(<&6Rn!LRXR2-W`8{>p>fOUiFFU&_t*S~&X;XS5&wprCQqfECR7&

+QX+GJWYqG2}Cl4_uKr+kdr1)mTcMXs#iJgYi^iqAj(dO-)DB zO7F(rKH=HIEE{=y(c~u8y1^Iqk4i_7&-AE62WCoh zO@CPpugC{oVWXyP1nLV%;*(~{)`aI8Qi1i?*}(tOJuL~7r}{!ctAtpINyl?_kF;z>2t}0R>z0>l_rnXH|YNU6Z-f4N;(YuNI^aVgx4)taC`332R)KE~NXEGSXX0*`zRtN)ZU4ILi8YCcTDS$cX55{p5XX;sXI}}xf;A9K7o_9 z;C0pqkF68DW?9I?o%@KH>>rD=p7FG+#PVjQx`(wua>;~7S#S1_6$TgHS0 zTE^A%=hR-t^usWi*K09ou8)D7zJV{o>5Bv^3v;$-|Bu+c|92?&v19)#l!CNBK>zO% zi~fXYgc2Qn5P4AZw+YdIkRgZ9u)m?uhb25eRb?#t+MybW1z4o<+te>p|4>Vc`Byg( z$zvw?;p=KoqaSAFkbVP$HgmLaoZ#r-xXIDU5#vxf5*!;iKH}KPQSzteIJR)K za-8Jo=P>^RP)i302H^N|Lk0i) zC9}`$+2=cZe|zt9&OiVD_6LAYe8teAb4ykYGo#Q`w@gK~gscf8t5`fE_vnT-$<6Ee zTyB4^&;2ZcF@&d-Sw+q&`hH!@savqrC?PG=0m zlprzqXSu08nqxR0OKiEosHy0JMw+4b8)gxwyJ?K!WWq2fX=jKKYEWAa4~{S#nBY1$DZqcY zu34Hw3_jf^$;7xd*P~}jD|W}XSkAHR-r$V2LdhC_dh=Q~!%d2=%y>)=1<*isXP1dN z7r=3bOMA{uG)XnaC7dLns~Sc33S~MLw^M%#r-O*%48!H!ByV(XhDUEYJY}P|r#oFl`%Lac+|J;Q6&CLdq66osE_Pb{ zx?(93E-*CjKKT+ZF$Aa@oi7XxTNYn|j}We)D~QXWG#}ZRlCT_m*G<-ui-&ah;+3v1Wfw#x0(s z{ozJzeB<7-=L!#@A2;oi7@%4gc8M}v+565@sBR+i>|VN+VP8&}(A434F3n8_7H2rR zZO$kWwC~PPsL6t_E!z@Y7!`bLiU%;rQ0wmJb{F+2!n*N9NNXT`SCVr@lw+BT!7&2b@sy9`ya&G-LPcq52W++)~J zr8cyh7V4gO2ybHC-X-oc>^fwI)4Q{j@GuCU-I*JwTiD~~7bj6h{OO>dbFhQF8R`-7fZ z&&oeglwLtC26WiRu5EJ? z{9)H>NfaU;2a_=+dz3AGyS7{o22i||Wv_M-s_$E$t}Vc36Rp&!R_n>?~TwuuWNMzfpN zcx`iW!0W!C6v`dE?s9V9$qP{55*eW&T2gG`FlLC?cgEq3!z}zDif=ez3)~SH6nG#|?kGb7 z)dJ51o(sJHA5cpN2m%QwIbsF?0N4%y08mQ<1Qe6#78R3J6jOhvj_V{ep+FP2yjm9N z29qLIR7g?6BO(tJN~J1`XcCWt6F($-5{X^Eg%wL43nY+G)g>Q=xMNZwR9KYJwddY5 z=gzrvzW@004Zt4mFx(G?bjDHaa{7ld=3|*SUK06S3fHNJk$f%U<~#`AG?vHDWfqK~ zIOPlO_&l6Az0rSEj0x2l%JpvV;MwE;u=%RfIU2s`_n!9aonfme61UVfcxyvP|A+%tjlda^9HcB%vu$f9Ug4`NI_2hrpvgp&wnx&cgcBy!BY-{(} z>084j+|HOQ_gFrddX)GqvrEGi7femZ*|n5&!an+p z>z|POaIRUyb}Dy+=s<(eOEk{f0D23>UxCtDb(sR|EnLCX0a$`-*cf0XQDG!76RajE z{{&D=2ME>FecZE;7E%QSQ#8DOljR%n2?pT!azh3H0BMuo9BT;z2`4#X1^@uqlT#gK z9o5u*-01-T096A30672&00000000000000$6abU)0Tq+~1RawH9y0002jQd2Yl diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt index e08eca3e84..c29806f5f2 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionEncumbranceTests.kt @@ -1,12 +1,14 @@ package net.corda.core.contracts import net.corda.contracts.asset.Cash +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.utilities.DUMMY_PUBKEY_1 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 java.security.PublicKey import java.time.Instant import java.time.temporal.ChronoUnit @@ -42,7 +44,7 @@ class TransactionEncumbranceTests { data class State( val validFrom: Instant ) : ContractState { - override val participants: List = emptyList() + override val participants: List = emptyList() override val contract: Contract = TEST_TIMELOCK_ID } } diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionGraphSearchTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionGraphSearchTests.kt index cc58e420a5..3bea04fc0a 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionGraphSearchTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionGraphSearchTests.kt @@ -1,12 +1,13 @@ package net.corda.core.contracts import net.corda.core.crypto.newSecureRandom +import net.corda.core.crypto.tree import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.DUMMY_NOTARY 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.node.MockTransactionStorage import org.junit.Test import java.security.KeyPair import kotlin.test.assertEquals @@ -31,7 +32,7 @@ class TransactionGraphSearchTests { fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage { val originTx = TransactionType.General.Builder(DUMMY_NOTARY).apply { addOutputState(DummyState(random31BitValue())) - addCommand(command, signer.public) + addCommand(command, signer.public.tree) signWith(signer) signWith(DUMMY_NOTARY_KEY) }.toSignedTransaction(false) diff --git a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt b/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt index a692b5f2a6..b0db3d3559 100644 --- a/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt +++ b/core/src/test/kotlin/net/corda/core/contracts/TransactionTests.kt @@ -4,6 +4,7 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.core.crypto.Party import net.corda.core.crypto.SecureHash import net.corda.core.crypto.signWithECDSA +import net.corda.core.crypto.tree import net.corda.core.serialization.SerializedBytes import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.SignedTransaction @@ -29,7 +30,7 @@ class TransactionTests { outputs = emptyList(), commands = emptyList(), 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(), timestamp = null ) @@ -38,20 +39,20 @@ class TransactionTests { assertFailsWith { make().verifySignatures() } assertEquals( - setOf(DUMMY_KEY_1.public), + setOf(DUMMY_KEY_1.public.tree), assertFailsWith { make(DUMMY_KEY_2).verifySignatures() }.missing ) assertEquals( - setOf(DUMMY_KEY_2.public), + setOf(DUMMY_KEY_2.public.tree), assertFailsWith { make(DUMMY_KEY_1).verifySignatures() }.missing ) assertEquals( - setOf(DUMMY_KEY_2.public), - assertFailsWith { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public) }.missing + setOf(DUMMY_KEY_2.public.tree), + assertFailsWith { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public.tree) }.missing ) - make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public) - make(DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public) + make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public.tree) + make(DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public.tree) make(DUMMY_KEY_1, DUMMY_KEY_2).verifySignatures() } @@ -64,7 +65,7 @@ class TransactionTests { val commands = emptyList>() val attachments = emptyList() val id = SecureHash.randomSHA256() - val signers = listOf(DUMMY_NOTARY_KEY.public) + val signers = listOf(DUMMY_NOTARY_KEY.public.tree) val timestamp: Timestamp? = null val transaction: LedgerTransaction = LedgerTransaction( inputs, @@ -91,7 +92,7 @@ class TransactionTests { val commands = emptyList>() val attachments = emptyList() val id = SecureHash.randomSHA256() - val signers = listOf(DUMMY_NOTARY_KEY.public) + val signers = listOf(DUMMY_NOTARY_KEY.public.tree) val timestamp: Timestamp? = null val transaction: LedgerTransaction = LedgerTransaction( inputs, diff --git a/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt b/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt index 8f4ae07a1c..fe361e354b 100644 --- a/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/AttachmentClassLoaderTests.kt @@ -2,6 +2,7 @@ package net.corda.core.node import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.node.services.AttachmentStorage import net.corda.core.serialization.* @@ -15,7 +16,6 @@ import org.junit.Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.net.URLClassLoader -import java.security.PublicKey import java.util.jar.JarOutputStream import java.util.zip.ZipEntry import kotlin.test.assertEquals @@ -37,7 +37,7 @@ class AttachmentClassLoaderTests { class AttachmentDummyContract : Contract { data class State(val magicNumber: Int = 0) : ContractState { override val contract = ATTACHMENT_TEST_PROGRAM_ID - override val participants: List + override val participants: List get() = listOf() } diff --git a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt index e92e2c397f..14b10e2ce3 100644 --- a/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt +++ b/core/src/test/kotlin/net/corda/core/node/VaultUpdateTests.kt @@ -1,11 +1,11 @@ package net.corda.core.node import net.corda.core.contracts.* +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.node.services.Vault import net.corda.core.utilities.DUMMY_NOTARY import org.junit.Test -import java.security.PublicKey import kotlin.test.assertEquals @@ -20,7 +20,7 @@ class VaultUpdateTests { } private class DummyState : ContractState { - override val participants: List + override val participants: List get() = emptyList() override val contract = VaultUpdateTests.DummyContract } diff --git a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt index 9e262465a2..4aea91a608 100644 --- a/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt +++ b/core/src/test/kotlin/net/corda/core/serialization/TransactionSerializationTests.kt @@ -1,7 +1,9 @@ package net.corda.core.serialization 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.seconds import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.* @@ -9,7 +11,6 @@ import net.corda.testing.MINI_CORP import net.corda.testing.generateStateRef import org.junit.Before import org.junit.Test -import java.security.PublicKey import java.security.SignatureException import java.util.* import kotlin.test.assertEquals @@ -27,12 +28,12 @@ class TransactionSerializationTests { data class State( val deposit: PartyAndReference, val amount: Amount, - override val owner: PublicKey) : OwnableState { + override val owner: PublicKeyTree) : OwnableState { override val contract: Contract = TEST_PROGRAM_ID - override val participants: List + override val participants: List 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 { class Move() : TypeOnlyCommandData(), Commands @@ -45,7 +46,7 @@ class TransactionSerializationTests { val fakeStateRef = generateStateRef() 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 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 @@ -53,7 +54,7 @@ class TransactionSerializationTests { @Before fun setup() { 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. assertFailsWith(SignatureException::class) { 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_KEY_2) diff --git a/experimental/src/main/kotlin/net/corda/contracts/AccountReceivable.kt b/experimental/src/main/kotlin/net/corda/contracts/AccountReceivable.kt index 5bf6487502..d0fbb4ebe5 100644 --- a/experimental/src/main/kotlin/net/corda/contracts/AccountReceivable.kt +++ b/experimental/src/main/kotlin/net/corda/contracts/AccountReceivable.kt @@ -2,10 +2,9 @@ package net.corda.contracts import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.toStringShort import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey import java.time.LocalDate import java.time.ZoneOffset import java.util.* @@ -44,24 +43,24 @@ class AccountReceivable : Contract { data class State( // technical variables - override val owner: PublicKey, + override val owner: PublicKeyTree, val status: StatusEnum, val props: AccountReceivableProperties ) : OwnableState { override val contract = ACCOUNTRECEIVABLE_PROGRAM_ID - override val participants: List + override val participants: List get() = listOf(owner) - override fun toString() = "AR owned by ${owner.toStringShort()})" + override fun toString() = "AR owned by $owner)" fun checkInvoice(invoice: Invoice.State): Boolean { val arProps = Helper.invoicePropsToARProps(invoice.props, props.discountRate) 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)) } diff --git a/experimental/src/main/kotlin/net/corda/contracts/BillOfLadingAgreement.kt b/experimental/src/main/kotlin/net/corda/contracts/BillOfLadingAgreement.kt index c11cfff603..90a2c19a4a 100644 --- a/experimental/src/main/kotlin/net/corda/contracts/BillOfLadingAgreement.kt +++ b/experimental/src/main/kotlin/net/corda/contracts/BillOfLadingAgreement.kt @@ -2,10 +2,10 @@ package net.corda.contracts import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.days import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey import java.time.Instant import java.time.LocalDate @@ -46,15 +46,15 @@ class BillOfLadingAgreement : Contract { data class State( // technical variables - override val owner: PublicKey, + override val owner: PublicKeyTree, val beneficiary: Party, val props: BillOfLadingProperties ) : OwnableState { - override val participants: List + override val participants: List get() = listOf(owner) - override fun withNewOwner(newOwner: PublicKey): Pair { + override fun withNewOwner(newOwner: PublicKeyTree): Pair { return Pair(Commands.TransferPossession(), copy(owner = newOwner)) } @@ -112,7 +112,7 @@ class BillOfLadingAgreement : Contract { /** * 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 builder = TransactionType.General.Builder(notary = notary) 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. */ - fun generateTransferAndEndorse(tx: TransactionBuilder, BoL: StateAndRef, newOwner: PublicKey, newBeneficiary: Party) { + fun generateTransferAndEndorse(tx: TransactionBuilder, BoL: StateAndRef, newOwner: PublicKeyTree, newBeneficiary: Party) { tx.addInputState(BoL) tx.addOutputState(BoL.state.data.copy(owner = newOwner, beneficiary = newBeneficiary)) - val signers: List = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey) + val signers: List = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey) tx.addCommand(Commands.TransferAndEndorseBL(), signers) } /** * Updates the given partial transaction with an input/output/command to reassign ownership of the paper. */ - fun generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef, newOwner: PublicKey) { + fun generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef, newOwner: PublicKeyTree) { tx.addInputState(BoL) tx.addOutputState(BoL.state.data.copy(owner = newOwner)) // tx.addOutputState(BoL.state.data.copy().withNewOwner(newOwner)) diff --git a/experimental/src/main/kotlin/net/corda/contracts/Invoice.kt b/experimental/src/main/kotlin/net/corda/contracts/Invoice.kt index fadef79213..64278d8eec 100644 --- a/experimental/src/main/kotlin/net/corda/contracts/Invoice.kt +++ b/experimental/src/main/kotlin/net/corda/contracts/Invoice.kt @@ -3,6 +3,7 @@ package net.corda.contracts import com.fasterxml.jackson.annotation.JsonIgnoreProperties import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.transactions.TransactionBuilder import java.security.PublicKey @@ -71,7 +72,7 @@ class Invoice : Contract { override val contract = INVOICE_PROGRAM_ID - override val participants: List + override val participants: List get() = listOf(owner.owningKey) // returns true when the actual business properties of the @@ -86,7 +87,7 @@ class Invoice : Contract { val amount: Amount> get() = props.amount override fun isRelevant(ourKeys: Set): Boolean { - return owner.owningKey in ourKeys || buyer.owningKey in ourKeys + return owner.owningKey.containsAny(ourKeys) || buyer.owningKey.containsAny(ourKeys) } } diff --git a/experimental/src/main/kotlin/net/corda/contracts/LCApplication.kt b/experimental/src/main/kotlin/net/corda/contracts/LCApplication.kt index ad3f440d29..bcee5e25d9 100644 --- a/experimental/src/main/kotlin/net/corda/contracts/LCApplication.kt +++ b/experimental/src/main/kotlin/net/corda/contracts/LCApplication.kt @@ -1,11 +1,11 @@ package net.corda.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.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey import java.time.LocalDate import java.time.Period import java.util.* @@ -108,14 +108,14 @@ class LCApplication : Contract { } data class State( - val owner: PublicKey, + val owner: PublicKeyTree, val status: Status, val props: LCApplicationProperties ) : ContractState { override val contract = LC_APPLICATION_PROGRAM_ID - override val participants: List + override val participants: List get() = listOf(owner) // 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 - fun withoutOwner() = copy(owner = NullPublicKey) + fun withoutOwner() = copy(owner = NullPublicKeyTree) } fun generateApply(props: LCApplicationProperties, notary: Party, purchaseOrder: Attachment): TransactionBuilder { diff --git a/experimental/src/main/kotlin/net/corda/contracts/LOC.kt b/experimental/src/main/kotlin/net/corda/contracts/LOC.kt index 2d163bf0f4..725c9fdece 100644 --- a/experimental/src/main/kotlin/net/corda/contracts/LOC.kt +++ b/experimental/src/main/kotlin/net/corda/contracts/LOC.kt @@ -3,10 +3,10 @@ package net.corda.contracts import net.corda.contracts.asset.sumCashBy import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.days import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey import java.time.Instant import java.time.LocalDate import java.time.Period @@ -58,7 +58,7 @@ class LOC : Contract { ) : ContractState { override val contract = LOC_PROGRAM_ID - override val participants: List + override val participants: List get() = listOf() } diff --git a/experimental/src/main/kotlin/net/corda/contracts/universal/UniversalContract.kt b/experimental/src/main/kotlin/net/corda/contracts/universal/UniversalContract.kt index 172a699826..b93a327dc0 100644 --- a/experimental/src/main/kotlin/net/corda/contracts/universal/UniversalContract.kt +++ b/experimental/src/main/kotlin/net/corda/contracts/universal/UniversalContract.kt @@ -2,10 +2,10 @@ package net.corda.contracts.universal import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.transactions.TransactionBuilder import java.math.BigDecimal -import java.security.PublicKey import java.time.Instant /** @@ -18,7 +18,7 @@ val UNIVERSAL_PROGRAM_ID = UniversalContract() class UniversalContract : Contract { - data class State(override val participants: List, + data class State(override val participants: List, val details: Arrangement) : ContractState { override val contract = UNIVERSAL_PROGRAM_ID } @@ -316,7 +316,7 @@ class UniversalContract : Contract { override val legalContractReference: SecureHash 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()) tx.addOutputState(State(listOf(notary), arrangement)) tx.addCommand(Commands.Issue(), at.party.owningKey) diff --git a/experimental/src/main/kotlin/net/corda/contracts/universal/Util.kt b/experimental/src/main/kotlin/net/corda/contracts/universal/Util.kt index 2a5c0381f1..f31e1f9b38 100644 --- a/experimental/src/main/kotlin/net/corda/contracts/universal/Util.kt +++ b/experimental/src/main/kotlin/net/corda/contracts/universal/Util.kt @@ -2,15 +2,11 @@ package net.corda.contracts.universal import com.google.common.collect.ImmutableSet import com.google.common.collect.Sets -import net.corda.core.contracts.Amount import net.corda.core.contracts.Frequency import net.corda.core.crypto.Party -import com.sun.org.apache.xpath.internal.operations.Bool -import java.math.BigDecimal -import java.security.PublicKey +import net.corda.core.crypto.PublicKeyTree import java.time.Instant import java.time.LocalDate -import java.util.* /** * 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) -private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet = +private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet = when (arrangement) { - is Zero -> ImmutableSet.of() + is Zero -> ImmutableSet.of() is Transfer -> ImmutableSet.of(arrangement.from.owningKey) is And -> - arrangement.arrangements.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() + arrangement.arrangements.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() is Actions -> - arrangement.actions.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() + arrangement.actions.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build() is RollOut -> liablePartiesVisitor(arrangement.template) - is Continuation -> ImmutableSet.of() + is Continuation -> ImmutableSet.of() else -> throw IllegalArgumentException("liableParties " + arrangement) } -private fun liablePartiesVisitor(action: Action): ImmutableSet = +private fun liablePartiesVisitor(action: Action): ImmutableSet = if (action.actors.size != 1) liablePartiesVisitor(action.arrangement) else Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy() /** returns list of potentially liable parties for a given contract */ -fun liableParties(contract: Arrangement): Set = liablePartiesVisitor(contract) +fun liableParties(contract: Arrangement): Set = liablePartiesVisitor(contract) -private fun involvedPartiesVisitor(action: Action): Set = +private fun involvedPartiesVisitor(action: Action): Set = Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy() -private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet = +private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet = when (arrangement) { - is Zero -> ImmutableSet.of() + is Zero -> ImmutableSet.of() is Transfer -> ImmutableSet.of(arrangement.from.owningKey) is And -> - arrangement.arrangements.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() + arrangement.arrangements.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() is Actions -> - arrangement.actions.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() + arrangement.actions.fold(ImmutableSet.builder(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build() else -> throw IllegalArgumentException() } /** returns list of involved parties for a given contract */ -fun involvedParties(arrangement: Arrangement): Set = involvedPartiesVisitor(arrangement) +fun involvedParties(arrangement: Arrangement): Set = involvedPartiesVisitor(arrangement) fun replaceParty(action: Action, from: Party, to: Party): Action = if (action.actors.contains(from)) { diff --git a/finance/isolated/src/main/kotlin/net/corda/contracts/AnotherDummyContract.kt b/finance/isolated/src/main/kotlin/net/corda/contracts/AnotherDummyContract.kt index 92d171d1ab..cf5d4e75fb 100644 --- a/finance/isolated/src/main/kotlin/net/corda/contracts/AnotherDummyContract.kt +++ b/finance/isolated/src/main/kotlin/net/corda/contracts/AnotherDummyContract.kt @@ -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 import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey // 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 { data class State(val magicNumber: Int = 0) : ContractState { override val contract = ANOTHER_DUMMY_PROGRAM_ID - override val participants: List + override val participants: List get() = emptyList() } diff --git a/finance/src/main/java/net/corda/contracts/ICommercialPaperState.java b/finance/src/main/java/net/corda/contracts/ICommercialPaperState.java index d8e92976b2..b818ca7be5 100644 --- a/finance/src/main/java/net/corda/contracts/ICommercialPaperState.java +++ b/finance/src/main/java/net/corda/contracts/ICommercialPaperState.java @@ -1,10 +1,13 @@ 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.*; -import java.util.*; +import java.time.Instant; +import java.util.Currency; /* 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. @@ -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). */ public interface ICommercialPaperState extends ContractState { - ICommercialPaperState withOwner(PublicKey newOwner); + ICommercialPaperState withOwner(PublicKeyTree newOwner); ICommercialPaperState withIssuance(PartyAndReference newIssuance); diff --git a/finance/src/main/java/net/corda/contracts/JavaCommercialPaper.java b/finance/src/main/java/net/corda/contracts/JavaCommercialPaper.java index f309b37366..279396920d 100644 --- a/finance/src/main/java/net/corda/contracts/JavaCommercialPaper.java +++ b/finance/src/main/java/net/corda/contracts/JavaCommercialPaper.java @@ -1,24 +1,34 @@ package net.corda.contracts; -import com.google.common.collect.*; -import net.corda.contracts.asset.*; +import com.google.common.collect.ImmutableList; +import kotlin.Pair; +import kotlin.Unit; +import net.corda.contracts.asset.CashKt; import net.corda.core.contracts.*; -import net.corda.core.contracts.Timestamp; -import net.corda.core.contracts.TransactionForContract.*; -import net.corda.core.contracts.clauses.*; -import net.corda.core.node.services.*; -import net.corda.core.transactions.*; -import kotlin.*; -import net.corda.core.crypto.*; -import org.jetbrains.annotations.*; +import net.corda.core.contracts.TransactionForContract.InOutGroup; +import net.corda.core.contracts.clauses.AnyComposition; +import net.corda.core.contracts.clauses.Clause; +import net.corda.core.contracts.clauses.ClauseVerifier; +import net.corda.core.contracts.clauses.GroupClauseVerifier; +import net.corda.core.crypto.CryptoUtilitiesKt; +import net.corda.core.crypto.Party; +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.*; -import java.util.*; -import java.util.stream.*; +import java.time.Instant; +import java.util.Collections; +import java.util.Currency; +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.*; +import static kotlin.collections.CollectionsKt.single; +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 { private PartyAndReference issuance; - private PublicKey owner; + private PublicKeyTree owner; private Amount> faceValue; private Instant maturityDate; public State() { } // For serialization - public State(PartyAndReference issuance, PublicKey owner, Amount> faceValue, + public State(PartyAndReference issuance, PublicKeyTree owner, Amount> faceValue, Instant maturityDate) { this.issuance = issuance; this.owner = owner; @@ -49,13 +59,13 @@ public class JavaCommercialPaper implements Contract { 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); } @NotNull @Override - public Pair withNewOwner(@NotNull PublicKey newOwner) { + public Pair withNewOwner(@NotNull PublicKeyTree newOwner) { 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 - public PublicKey getOwner() { + public PublicKeyTree getOwner() { return owner; } @@ -117,12 +127,12 @@ public class JavaCommercialPaper implements Contract { } public State withoutOwner() { - return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate); + return new State(issuance, CryptoUtilitiesKt.getNullPublicKeyTree(), faceValue, maturityDate); } @NotNull @Override - public List getParticipants() { + public List getParticipants() { 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())); } - public void generateMove(TransactionBuilder tx, StateAndRef paper, PublicKey newOwner) { + public void generateMove(TransactionBuilder tx, StateAndRef paper, PublicKeyTree newOwner) { 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.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner())); diff --git a/finance/src/main/kotlin/net/corda/contracts/CommercialPaper.kt b/finance/src/main/kotlin/net/corda/contracts/CommercialPaper.kt index 3c07d09c5c..a14c0ec68f 100644 --- a/finance/src/main/kotlin/net/corda/contracts/CommercialPaper.kt +++ b/finance/src/main/kotlin/net/corda/contracts/CommercialPaper.kt @@ -1,8 +1,5 @@ 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.clause.AbstractIssue 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.verifyClause import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree 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.random63BitValue 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.utilities.Emoji import net.corda.schemas.CommercialPaperSchemaV1 -import java.security.PublicKey import java.time.Instant import java.util.* @@ -64,22 +59,22 @@ class CommercialPaper : Contract { data class State( val issuance: PartyAndReference, - override val owner: PublicKey, + override val owner: PublicKeyTree, val faceValue: Amount>, val maturityDate: Instant ) : OwnableState, QueryableState, ICommercialPaperState { override val contract = CP_PROGRAM_ID - override val participants: List + override val participants: List get() = listOf(owner) val token: Issued get() = Issued(issuance, Terms(faceValue.token, maturityDate)) - override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) - override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by ${owner.toStringShort()})" + 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)" // 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 withFaceValue(newFaceValue: Amount>): 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. */ - fun generateMove(tx: TransactionBuilder, paper: StateAndRef, newOwner: PublicKey) { + fun generateMove(tx: TransactionBuilder, paper: StateAndRef, newOwner: PublicKeyTree) { tx.addInputState(paper) tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary)) 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 ICommercialPaperState.`owned by`(newOwner: PublicKey) = withOwner(newOwner) +infix fun ICommercialPaperState.`owned by`(newOwner: PublicKeyTree) = withOwner(newOwner) diff --git a/finance/src/main/kotlin/net/corda/contracts/CommercialPaperLegacy.kt b/finance/src/main/kotlin/net/corda/contracts/CommercialPaperLegacy.kt index ceeff28a24..7adf27c70e 100644 --- a/finance/src/main/kotlin/net/corda/contracts/CommercialPaperLegacy.kt +++ b/finance/src/main/kotlin/net/corda/contracts/CommercialPaperLegacy.kt @@ -1,18 +1,14 @@ 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.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.PublicKeyTree 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.transactions.TransactionBuilder import net.corda.core.utilities.Emoji -import java.security.PublicKey import java.time.Instant import java.util.* @@ -30,19 +26,19 @@ class CommercialPaperLegacy : Contract { data class State( val issuance: PartyAndReference, - override val owner: PublicKey, + override val owner: PublicKeyTree, val faceValue: Amount>, val maturityDate: Instant ) : OwnableState, ICommercialPaperState { override val contract = CP_LEGACY_PROGRAM_ID override val participants = listOf(owner) - fun withoutOwner() = copy(owner = NullPublicKey) - override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner)) - override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by ${owner.toStringShort()})" + fun withoutOwner() = copy(owner = NullPublicKeyTree) + 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)" // 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 withFaceValue(newFaceValue: Amount>): ICommercialPaperState = copy(faceValue = newFaceValue) @@ -119,7 +115,7 @@ class CommercialPaperLegacy : Contract { return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey)) } - fun generateMove(tx: TransactionBuilder, paper: StateAndRef, newOwner: PublicKey) { + fun generateMove(tx: TransactionBuilder, paper: StateAndRef, newOwner: PublicKeyTree) { tx.addInputState(paper) tx.addOutputState(paper.state.data.withOwner(newOwner)) tx.addCommand(Command(Commands.Move(), paper.state.data.owner)) diff --git a/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt b/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt index 82714be55f..5bee4fd506 100644 --- a/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt +++ b/finance/src/main/kotlin/net/corda/contracts/asset/Cash.kt @@ -9,16 +9,13 @@ import net.corda.core.contracts.clauses.FirstComposition import net.corda.core.contracts.clauses.GroupClauseVerifier import net.corda.core.contracts.clauses.verifyClause import net.corda.core.crypto.* -import net.corda.core.node.services.Vault import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import net.corda.core.schemas.QueryableState import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.Emoji import net.corda.schemas.CashSchemaV1 -import net.corda.core.crypto.* import java.math.BigInteger -import java.security.PublicKey import java.util.* ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -85,22 +82,22 @@ class Cash : OnLedgerAsset() { override val amount: 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 ) : FungibleAsset, QueryableState { - constructor(deposit: PartyAndReference, amount: Amount, owner: PublicKey) + constructor(deposit: PartyAndReference, amount: Amount, owner: PublicKeyTree) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) override val exitKeys = setOf(owner, amount.token.issuer.party.owningKey) override val contract = CASH_PROGRAM_ID override val participants = listOf(owner) - override fun move(newAmount: Amount>, newOwner: PublicKey): FungibleAsset + override fun move(newAmount: Amount>, newOwner: PublicKeyTree): FungibleAsset = 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. */ override fun generateMappedObject(schema: MappedSchema): PersistentState { @@ -148,13 +145,13 @@ class Cash : OnLedgerAsset() { /** * Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey. */ - fun generateIssue(tx: TransactionBuilder, tokenDef: Issued, pennies: Long, owner: PublicKey, notary: Party) + fun generateIssue(tx: TransactionBuilder, tokenDef: Issued, pennies: Long, owner: PublicKeyTree, notary: Party) = 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. */ - fun generateIssue(tx: TransactionBuilder, amount: Amount>, owner: PublicKey, notary: Party) { + fun generateIssue(tx: TransactionBuilder, amount: Amount>, owner: PublicKeyTree, notary: Party) { check(tx.inputStates().isEmpty()) check(tx.outputStates().map { it.data }.sumCashOrNull() == null) val at = amount.token.issuer @@ -162,7 +159,7 @@ class Cash : OnLedgerAsset() { tx.addCommand(generateIssueCommand(), at.party.owningKey) } - override fun deriveState(txState: TransactionState, amount: Amount>, owner: PublicKey) + override fun deriveState(txState: TransactionState, amount: Amount>, owner: PublicKeyTree) = txState.copy(data = txState.data.copy(amount = amount, owner = owner)) override fun generateExitCommand(amount: Amount>) = Commands.Exit(amount) override fun generateIssueCommand() = Commands.Issue() @@ -179,7 +176,7 @@ class Cash : OnLedgerAsset() { * if there are none, or if any of the cash states cannot be added together (i.e. are * different currencies or issuers). */ -fun Iterable.sumCashBy(owner: PublicKey): Amount> = filterIsInstance().filter { it.owner == owner }.map { it.amount }.sumOrThrow() +fun Iterable.sumCashBy(owner: PublicKeyTree): Amount> = filterIsInstance().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 @@ -195,12 +192,12 @@ fun Iterable.sumCashOrZero(currency: Issued): Amount().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(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))) -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`(deposit: PartyAndReference) = issuedBy(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. */ val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) } /** 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 */ -val Amount.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey) +val Amount.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] */ -val Amount>.STATE: Cash.State get() = Cash.State(this, NullPublicKey) +val Amount>.STATE: Cash.State get() = Cash.State(this, NullPublicKeyTree) diff --git a/finance/src/main/kotlin/net/corda/contracts/asset/CommodityContract.kt b/finance/src/main/kotlin/net/corda/contracts/asset/CommodityContract.kt index e4ef17bea3..44e83da05d 100644 --- a/finance/src/main/kotlin/net/corda/contracts/asset/CommodityContract.kt +++ b/finance/src/main/kotlin/net/corda/contracts/asset/CommodityContract.kt @@ -4,15 +4,14 @@ import net.corda.contracts.clause.AbstractConserveAmount import net.corda.contracts.clause.AbstractIssue import net.corda.contracts.clause.NoZeroSizedOutputs 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.GroupClauseVerifier import net.corda.core.contracts.clauses.verifyClause import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.crypto.newSecureRandom -import net.corda.core.crypto.toStringShort import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey import java.util.* ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -90,27 +89,27 @@ class CommodityContract : OnLedgerAsset() } - + /** A state representing a commodity claim against some party */ data class State( override val amount: Amount>, /** There must be a MoveCommand signed by this key to claim the amount */ - override val owner: PublicKey + override val owner: PublicKeyTree ) : FungibleAsset { - constructor(deposit: PartyAndReference, amount: Amount, owner: PublicKey) + constructor(deposit: PartyAndReference, amount: Amount, owner: PublicKeyTree) : this(Amount(amount.quantity, Issued(deposit, amount.token)), owner) override val contract = COMMODITY_PROGRAM_ID override val exitKeys = Collections.singleton(owner) override val participants = listOf(owner) - override fun move(newAmount: Amount>, newOwner: PublicKey): FungibleAsset + override fun move(newAmount: Amount>, newOwner: PublicKeyTree): FungibleAsset = 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 @@ -144,13 +143,13 @@ class CommodityContract : OnLedgerAsset, pennies: Long, owner: PublicKey, notary: Party) + fun generateIssue(tx: TransactionBuilder, tokenDef: Issued, pennies: Long, owner: PublicKeyTree, notary: Party) = 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. */ - fun generateIssue(tx: TransactionBuilder, amount: Amount>, owner: PublicKey, notary: Party) { + fun generateIssue(tx: TransactionBuilder, amount: Amount>, owner: PublicKeyTree, notary: Party) { check(tx.inputStates().isEmpty()) check(tx.outputStates().map { it.data }.sumCashOrNull() == null) val at = amount.token.issuer @@ -159,7 +158,7 @@ class CommodityContract : OnLedgerAsset, amount: Amount>, owner: PublicKey) + override fun deriveState(txState: TransactionState, amount: Amount>, owner: PublicKeyTree) = txState.copy(data = txState.data.copy(amount = amount, owner = owner)) override fun generateExitCommand(amount: Amount>) = Commands.Exit(amount) override fun generateIssueCommand() = Commands.Issue() diff --git a/finance/src/main/kotlin/net/corda/contracts/asset/Obligation.kt b/finance/src/main/kotlin/net/corda/contracts/asset/Obligation.kt index 8f73577360..1a1f1efbfb 100644 --- a/finance/src/main/kotlin/net/corda/contracts/asset/Obligation.kt +++ b/finance/src/main/kotlin/net/corda/contracts/asset/Obligation.kt @@ -1,16 +1,18 @@ package net.corda.contracts.asset import com.google.common.annotations.VisibleForTesting +import net.corda.contracts.asset.Obligation.Lifecycle.NORMAL import net.corda.contracts.clause.* import net.corda.core.contracts.* import net.corda.core.contracts.clauses.* import net.corda.core.crypto.* import net.corda.core.random63BitValue import net.corda.core.transactions.TransactionBuilder -import net.corda.core.utilities.* -import net.corda.core.crypto.* +import net.corda.core.utilities.Emoji +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.security.PublicKey import java.time.Duration import java.time.Instant import java.util.* @@ -267,21 +269,21 @@ class Obligation

: Contract { val template: Terms

, val quantity: Long, /** The public key of the entity the contract pays to */ - val beneficiary: PublicKey + val beneficiary: PublicKeyTree ) : FungibleAsset>, NettableState, MultilateralNetState

> { override val amount: Amount>> = Amount(quantity, Issued(obligor.ref(0), template)) override val contract = OBLIGATION_PROGRAM_ID - override val exitKeys: Collection = setOf(beneficiary) + override val exitKeys: Collection = setOf(beneficiary) val dueBefore: Instant = template.dueBefore - override val participants: List = listOf(obligor.owningKey, beneficiary) - override val owner: PublicKey = beneficiary + override val participants: List = listOf(obligor.owningKey, beneficiary) + override val owner: PublicKeyTree = beneficiary - override fun move(newAmount: Amount>>, newOwner: PublicKey): State

+ override fun move(newAmount: Amount>>, newOwner: PublicKeyTree): State

= copy(quantity = newAmount.quantity, beneficiary = newOwner) override fun toString() = when (lifecycle) { - Lifecycle.NORMAL -> "${Emoji.bagOfCash}Debt($amount due $dueBefore to ${beneficiary.toStringShort()})" - Lifecycle.DEFAULTED -> "${Emoji.bagOfCash}Debt($amount unpaid by $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)" } override val bilateralNetState: BilateralNetState

@@ -309,7 +311,7 @@ class Obligation

: 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 @@ -414,7 +416,7 @@ class Obligation

: Contract { * and same parties involved). */ fun generateCloseOutNetting(tx: TransactionBuilder, - signer: PublicKey, + signer: PublicKeyTree, vararg states: State

) { val netState = states.firstOrNull()?.bilateralNetState @@ -442,7 +444,7 @@ class Obligation

: Contract { */ @Suppress("unused") fun generateExit(tx: TransactionBuilder, amountIssued: Amount>>, - assetStates: List>>): PublicKey + assetStates: List>>): PublicKeyTree = Clauses.ConserveAmount

().generateExit(tx, amountIssued, assetStates, deriveState = { state, amount, owner -> state.copy(data = state.data.move(amount, owner)) }, generateMoveCommand = { -> Commands.Move() }, @@ -456,7 +458,7 @@ class Obligation

: Contract { obligor: Party, issuanceDef: Terms

, pennies: Long, - beneficiary: PublicKey, + beneficiary: PublicKeyTree, notary: Party) { check(tx.inputStates().isEmpty()) check(tx.outputStates().map { it.data }.sumObligationsOrNull

() == null) @@ -472,7 +474,7 @@ class Obligation

: Contract { "all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL }) } val groups = states.groupBy { it.multilateralNetState } - val partyLookup = HashMap() + val partyLookup = HashMap() 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. @@ -516,7 +518,7 @@ class Obligation

: Contract { // Produce a new set of states val groups = statesAndRefs.groupBy { it.state.data.amount.token } for ((aggregateState, stateAndRefs) in groups) { - val partiesUsed = ArrayList() + val partiesUsed = ArrayList() stateAndRefs.forEach { stateAndRef -> val outState = stateAndRef.state.data.copy(lifecycle = lifecycle) tx.addInputState(stateAndRef) @@ -561,7 +563,7 @@ class Obligation

: Contract { val template: Terms

= issuanceDef.product val obligationTotal: Amount

= Amount(states.map { it.data }.sumObligations

().quantity, template.product) var obligationRemaining: Amount

= obligationTotal - val assetSigners = HashSet() + val assetSigners = HashSet() statesAndRefs.forEach { tx.addInputState(it) } @@ -613,8 +615,8 @@ class Obligation

: Contract { * * @return a map of obligor/beneficiary pairs to the balance due. */ -fun

extractAmountsDue(product: Obligation.Terms

, states: Iterable>): Map, Amount>> { - val balances = HashMap, Amount>>() +fun

extractAmountsDue(product: Obligation.Terms

, states: Iterable>): Map, Amount>> { + val balances = HashMap, Amount>>() states.forEach { state -> val key = Pair(state.obligor.owningKey, state.beneficiary) @@ -628,8 +630,8 @@ fun

extractAmountsDue(product: Obligation.Terms

, states: Iterable netAmountsDue(balances: Map, Amount

>): Map, Amount

> { - val nettedBalances = HashMap, Amount

>() +fun

netAmountsDue(balances: Map, Amount

>): Map, Amount

> { + val nettedBalances = HashMap, Amount

>() balances.forEach { balance -> val (obligor, beneficiary) = balance.key @@ -653,8 +655,8 @@ fun

netAmountsDue(balances: Map, Amount

>): Map * @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being * returned. */ -fun

sumAmountsDue(balances: Map, Amount

>): Map { - val sum = HashMap() +fun

sumAmountsDue(balances: Map, Amount

>): Map { + val sum = HashMap() // Fill the map with zeroes initially balances.keys.forEach { @@ -695,19 +697,19 @@ fun

Iterable.sumObligationsOrZero(issuanceDef: Issued>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef) infix fun Obligation.State.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore)) -infix fun Obligation.State.between(parties: Pair) = copy(obligor = parties.first, beneficiary = parties.second) -infix fun Obligation.State.`owned by`(owner: PublicKey) = copy(beneficiary = owner) +infix fun Obligation.State.between(parties: Pair) = copy(obligor = parties.first, beneficiary = parties.second) +infix fun Obligation.State.`owned by`(owner: PublicKeyTree) = copy(beneficiary = owner) infix fun Obligation.State.`issued by`(party: Party) = copy(obligor = party) // For Java users: -@Suppress("unused") fun Obligation.State.ownedBy(owner: PublicKey) = copy(beneficiary = owner) +@Suppress("unused") fun Obligation.State.ownedBy(owner: PublicKeyTree) = copy(beneficiary = owner) @Suppress("unused") fun Obligation.State.issuedBy(party: Party) = copy(obligor = party) /** A randomly generated key. */ val DUMMY_OBLIGATION_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) } /** 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.OBLIGATION_DEF: Obligation.Terms get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME) val Amount>.OBLIGATION: Obligation.State - 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) diff --git a/finance/src/main/kotlin/net/corda/contracts/asset/OnLedgerAsset.kt b/finance/src/main/kotlin/net/corda/contracts/asset/OnLedgerAsset.kt index 2ca65ee515..ff895b930b 100644 --- a/finance/src/main/kotlin/net/corda/contracts/asset/OnLedgerAsset.kt +++ b/finance/src/main/kotlin/net/corda/contracts/asset/OnLedgerAsset.kt @@ -2,9 +2,8 @@ package net.corda.contracts.asset import net.corda.contracts.clause.AbstractConserveAmount import net.corda.core.contracts.* -import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -40,7 +39,7 @@ abstract class OnLedgerAsset> : Co * @return the public key of the assets issuer, who must sign the transaction for it to be valid. */ fun generateExit(tx: TransactionBuilder, amountIssued: Amount>, - assetStates: List>): PublicKey + assetStates: List>): PublicKeyTree = conserveClause.generateExit(tx, amountIssued, assetStates, deriveState = { state, amount, owner -> deriveState(state, amount, owner) }, generateMoveCommand = { -> generateMoveCommand() }, @@ -56,5 +55,5 @@ abstract class OnLedgerAsset> : Co * 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. */ - abstract fun deriveState(txState: TransactionState, amount: Amount>, owner: PublicKey): TransactionState + abstract fun deriveState(txState: TransactionState, amount: Amount>, owner: PublicKeyTree): TransactionState } diff --git a/finance/src/main/kotlin/net/corda/contracts/clause/AbstractConserveAmount.kt b/finance/src/main/kotlin/net/corda/contracts/clause/AbstractConserveAmount.kt index 4a70d7c4a9..a6808b920e 100644 --- a/finance/src/main/kotlin/net/corda/contracts/clause/AbstractConserveAmount.kt +++ b/finance/src/main/kotlin/net/corda/contracts/clause/AbstractConserveAmount.kt @@ -2,8 +2,8 @@ package net.corda.contracts.clause import net.corda.core.contracts.* import net.corda.core.contracts.clauses.Clause +import net.corda.core.crypto.PublicKeyTree import net.corda.core.transactions.TransactionBuilder -import java.security.PublicKey import java.util.* /** @@ -47,9 +47,9 @@ abstract class AbstractConserveAmount, C : CommandData, T : */ fun generateExit(tx: TransactionBuilder, amountIssued: Amount>, assetStates: List>, - deriveState: (TransactionState, Amount>, PublicKey) -> TransactionState, + deriveState: (TransactionState, Amount>, PublicKeyTree) -> TransactionState, generateMoveCommand: () -> CommandData, - generateExitCommand: (Amount>) -> CommandData): PublicKey { + generateExitCommand: (Amount>) -> CommandData): PublicKeyTree { val owner = assetStates.map { it.state.data.owner }.toSet().single() val currency = amountIssued.token.product val amount = Amount(amountIssued.quantity, currency) @@ -92,7 +92,7 @@ abstract class AbstractConserveAmount, C : CommandData, T : val outputAmount: Amount> = outputs.sumFungibleOrZero(groupingKey) // If we want to remove assets from the ledger, that must be signed for by the issuer and owner. - val exitKeys: Set = inputs.flatMap { it.exitKeys }.toSet() + val exitKeys: Set = inputs.flatMap { it.exitKeys }.toSet() val exitCommand = matchedCommands.select>(parties = null, signers = exitKeys).filter { it.value.amount.token == groupingKey }.singleOrNull() val amountExitingLedger: Amount> = exitCommand?.value?.amount ?: Amount(0, groupingKey) diff --git a/finance/src/main/kotlin/net/corda/contracts/clause/Net.kt b/finance/src/main/kotlin/net/corda/contracts/clause/Net.kt index 90c2398cee..f1ee8c8b50 100644 --- a/finance/src/main/kotlin/net/corda/contracts/clause/Net.kt +++ b/finance/src/main/kotlin/net/corda/contracts/clause/Net.kt @@ -6,7 +6,7 @@ import net.corda.contracts.asset.extractAmountsDue import net.corda.contracts.asset.sumAmountsDue import net.corda.core.contracts.* 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 @@ -22,7 +22,7 @@ interface NetState

{ * Bilateral states are used in close-out netting. */ data class BilateralNetState

( - val partyKeys: Set, + val partyKeys: Set, override val template: Obligation.Terms

) : NetState

diff --git a/finance/src/main/kotlin/net/corda/contracts/testing/Generators.kt b/finance/src/main/kotlin/net/corda/contracts/testing/Generators.kt index 5f39e0a7f8..15f0c160ec 100644 --- a/finance/src/main/kotlin/net/corda/contracts/testing/Generators.kt +++ b/finance/src/main/kotlin/net/corda/contracts/testing/Generators.kt @@ -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.random.SourceOfRandomness 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.testing.* import net.corda.core.transactions.SignedTransaction @@ -22,7 +25,7 @@ class ContractStateGenerator : Generator(ContractState::class.jav override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState { return Cash.State( 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::class.java) { class CommandGenerator : Generator(Command::class.java) { override fun generate(random: SourceOfRandomness, status: GenerationStatus): Command { val signersGenerator = ArrayListGenerator() - signersGenerator.addComponentGenerators(listOf(PublicKeyGenerator())) - return Command(CommandDataGenerator().generate(random, status), PublicKeyGenerator().generate(random, status)) + signersGenerator.addComponentGenerators(listOf(PublicKeyTreeGenerator())) + return Command(CommandDataGenerator().generate(random, status), PublicKeyTreeGenerator().generate(random, status)) } } diff --git a/finance/src/main/kotlin/net/corda/contracts/testing/VaultFiller.kt b/finance/src/main/kotlin/net/corda/contracts/testing/VaultFiller.kt index 24c41cce87..453490760f 100644 --- a/finance/src/main/kotlin/net/corda/contracts/testing/VaultFiller.kt +++ b/finance/src/main/kotlin/net/corda/contracts/testing/VaultFiller.kt @@ -7,16 +7,15 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY import net.corda.core.contracts.Amount import net.corda.core.contracts.Issued import net.corda.core.contracts.PartyAndReference -import net.corda.core.transactions.SignedTransaction import net.corda.core.contracts.TransactionType import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.node.ServiceHub import net.corda.core.node.services.Vault -import net.corda.core.protocols.StateMachineRunId import net.corda.core.serialization.OpaqueBytes +import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.DUMMY_NOTARY import java.security.KeyPair -import java.security.PublicKey import java.util.* @@ -36,12 +35,12 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount, atMostThisManyStates: Int = 10, rng: Random = Random(), ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })), - ownedBy: PublicKey? = null, + ownedBy: PublicKeyTree? = null, issuedBy: PartyAndReference = DUMMY_CASH_ISSUER, issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault { 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. val cash = Cash() diff --git a/finance/src/main/kotlin/net/corda/protocols/TwoPartyTradeProtocol.kt b/finance/src/main/kotlin/net/corda/protocols/TwoPartyTradeProtocol.kt index 09d8a8adac..21a42f6e9f 100644 --- a/finance/src/main/kotlin/net/corda/protocols/TwoPartyTradeProtocol.kt +++ b/finance/src/main/kotlin/net/corda/protocols/TwoPartyTradeProtocol.kt @@ -1,14 +1,10 @@ package net.corda.protocols import co.paralleluniverse.fibers.Suspendable -import net.corda.contracts.asset.Cash import net.corda.contracts.asset.sumCashBy import net.corda.core.contracts.* -import net.corda.core.crypto.DigitalSignature -import net.corda.core.crypto.Party -import net.corda.core.crypto.signWithECDSA +import net.corda.core.crypto.* import net.corda.core.node.NodeInfo -import net.corda.core.node.services.ServiceType import net.corda.core.protocols.ProtocolLogic import net.corda.core.seconds 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.trace import java.security.KeyPair -import java.security.PublicKey import java.util.* /** @@ -56,7 +51,7 @@ object TwoPartyTradeProtocol { data class SellerTradeInfo( val assetForSale: StateAndRef, val price: Amount, - val sellerOwnerKey: PublicKey + val sellerOwnerKey: PublicKeyTree ) data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey, @@ -104,8 +99,9 @@ object TwoPartyTradeProtocol { private fun receiveAndCheckProposedTransaction(): SignedTransaction { progressTracker.currentStep = AWAITING_PROPOSAL + val myPublicKey = myKeyPair.public.tree // 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(otherParty, hello) @@ -115,14 +111,14 @@ object TwoPartyTradeProtocol { progressTracker.nextStep() // 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}" } // Download and check all the things that this transaction depends on and verify it is contract-valid, // even though it is missing signatures. 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") // 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(otherParty, stx).unwrap { it } } - private fun signWithOurKeys(cashSigningPubKeys: List, ptx: TransactionBuilder): SignedTransaction { + private fun signWithOurKeys(cashSigningPubKeys: List, ptx: TransactionBuilder): SignedTransaction { // Now sign the transaction with whatever keys we need to move the cash. - for (k in cashSigningPubKeys) { - val priv = serviceHub.keyManagementService.toPrivate(k) - ptx.signWith(KeyPair(k, priv)) + for (publicKey in cashSigningPubKeys.keys) { + val privateKey = serviceHub.keyManagementService.toPrivate(publicKey) + ptx.signWith(KeyPair(publicKey, privateKey)) } return ptx.toSignedTransaction(checkSufficientSignatures = false) } - private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair> { + private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair> { 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 @@ -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 // initial seed in order to provide privacy protection. 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.addCommand(command, tradeRequest.assetForSale.state.data.owner) diff --git a/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt b/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt index b582921580..f400e6e535 100644 --- a/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt +++ b/finance/src/test/kotlin/net/corda/contracts/CommercialPaperTests.kt @@ -5,6 +5,7 @@ import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.core.contracts.* import net.corda.core.crypto.Party import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.tree import net.corda.core.days import net.corda.core.node.recordTransactions 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.utilities.configureDatabase import net.corda.node.utilities.databaseTransaction -import net.corda.testing.node.MockServices import net.corda.testing.* +import net.corda.testing.node.MockServices import net.corda.testing.node.makeTestDataSourceProperties import org.junit.Test import org.junit.runner.RunWith @@ -277,8 +278,8 @@ class CommercialPaperTestsGeneric { // Alice pays $9000 to BigCorp to own some of their debt. moveTX = run { val ptx = TransactionType.General.Builder(DUMMY_NOTARY) - aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public) - CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public) + aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public.tree) + CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public.tree) ptx.signWith(bigCorpServices.key) ptx.signWith(aliceServices.key) ptx.signWith(DUMMY_NOTARY_KEY) diff --git a/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt b/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt index 1f4b1240d6..ca012dae0c 100644 --- a/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt +++ b/finance/src/test/kotlin/net/corda/contracts/asset/CashTests.kt @@ -2,9 +2,7 @@ package net.corda.contracts.asset import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.core.contracts.* -import net.corda.core.crypto.Party -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.generateKeyPair +import net.corda.core.crypto.* import net.corda.core.node.services.Vault import net.corda.core.node.services.VaultService 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.makeTestDataSourceProperties import org.jetbrains.exposed.sql.Database -import org.junit.After import org.junit.Before import org.junit.Test import java.io.Closeable import java.security.KeyPair -import java.security.PublicKey import java.util.* import kotlin.test.* @@ -459,7 +455,7 @@ class CashTests { // Spend tx generation 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 @@ -485,7 +481,7 @@ class CashTests { return tx.toWireTransaction() } - fun makeSpend(amount: Amount, dest: PublicKey): WireTransaction { + fun makeSpend(amount: Amount, dest: PublicKeyTree): WireTransaction { val tx = TransactionType.General.Builder(DUMMY_NOTARY) databaseTransaction(database) { vault.generateSpend(tx, amount, dest) diff --git a/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt b/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt index dab8fc1494..6f52987f30 100644 --- a/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt +++ b/finance/src/test/kotlin/net/corda/contracts/asset/ObligationTests.kt @@ -2,13 +2,13 @@ package net.corda.contracts.asset import net.corda.contracts.asset.Obligation.Lifecycle 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.serialization.OpaqueBytes import net.corda.core.utilities.* import net.corda.testing.* import org.junit.Test -import java.security.PublicKey import java.time.Duration import java.time.temporal.ChronoUnit import java.util.* @@ -506,7 +506,7 @@ class ObligationTests { val oneUnitFcoj = Amount(1, defaultFcoj) val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME) val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE, - obligationDef, oneUnitFcoj.quantity, NullPublicKey) + obligationDef, oneUnitFcoj.quantity, NullPublicKeyTree) // Try settling a simple commodity obligation ledger { unverifiedTransaction { @@ -830,7 +830,7 @@ class ObligationTests { Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) ) - val expected: Map, Amount> = emptyMap() // Zero balances are stripped before returning + val expected: Map, Amount> = emptyMap() // Zero balances are stripped before returning val actual = netAmountsDue(balanced) assertEquals(expected, actual) } @@ -851,8 +851,8 @@ class ObligationTests { @Test fun `summing empty balances due between parties`() { - val empty = emptyMap, Amount>() - val expected = emptyMap() + val empty = emptyMap, Amount>() + val expected = emptyMap() val actual = sumAmountsDue(empty) assertEquals(expected, actual) } @@ -872,7 +872,7 @@ class ObligationTests { Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)), Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP)) ) - val expected: Map = emptyMap() // Zero balances are stripped before returning + val expected: Map = emptyMap() // Zero balances are stripped before returning val actual = sumAmountsDue(balanced) assertEquals(expected, actual) } diff --git a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt index 998afbde5f..f057835d61 100644 --- a/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/CordaRPCOpsImpl.kt @@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash import net.corda.core.contracts.* import net.corda.core.crypto.Party import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.keys import net.corda.core.crypto.toStringShort import net.corda.core.node.NodeInfo import net.corda.core.node.ServiceHub @@ -101,7 +102,7 @@ class CordaRPCOpsImpl( val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder, 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()}") builder.signWith(KeyPair(it, key)) } diff --git a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt index 8cbd428efc..7504b536d3 100644 --- a/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt +++ b/node/src/main/kotlin/net/corda/node/services/identity/InMemoryIdentityService.kt @@ -1,9 +1,9 @@ package net.corda.node.services.identity import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.node.services.IdentityService import net.corda.core.serialization.SingletonSerializeAsToken -import java.security.PublicKey import java.util.concurrent.ConcurrentHashMap import javax.annotation.concurrent.ThreadSafe @@ -12,7 +12,7 @@ import javax.annotation.concurrent.ThreadSafe */ @ThreadSafe class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService { - private val keyToParties = ConcurrentHashMap() + private val keyToParties = ConcurrentHashMap() private val nameToParties = ConcurrentHashMap() override fun registerIdentity(party: Party) { @@ -20,6 +20,6 @@ class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService { 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] } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt index bbb18aace7..44b09e131e 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingComponent.kt @@ -2,12 +2,11 @@ package net.corda.node.services.messaging import com.google.common.annotations.VisibleForTesting import com.google.common.net.HostAndPort -import net.corda.core.crypto.parsePublicKeyBase58 -import net.corda.core.crypto.toBase58String +import net.corda.core.crypto.PublicKeyTree import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.SingleMessageRecipient -import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.read +import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.node.services.config.NodeSSLConfiguration import net.corda.node.services.config.configureWithDevSSLCertificate 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.Path import java.security.KeyStore -import java.security.PublicKey /** * 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. * 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 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 */ abstract val config: NodeSSLConfiguration - protected fun parseKeyFromQueueName(name: String): PublicKey { + protected fun parseKeyFromQueueName(name: String): PublicKeyTree { 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 } diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index 6d3319cafe..a033a056a8 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -2,6 +2,7 @@ package net.corda.node.services.messaging import com.google.common.net.HostAndPort import net.corda.core.ThreadBox +import net.corda.core.crypto.PublicKeyTree import net.corda.core.messaging.* import net.corda.core.serialization.SerializedBytes 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.ResultRow import org.jetbrains.exposed.sql.statements.InsertStatement -import java.security.PublicKey import java.time.Instant import java.util.* import java.util.concurrent.CopyOnWriteArrayList @@ -49,7 +49,7 @@ import javax.annotation.concurrent.ThreadSafe @ThreadSafe class NodeMessagingClient(override val config: NodeConfiguration, val serverHostPort: HostAndPort, - val myIdentity: PublicKey?, + val myIdentity: PublicKeyTree?, val executor: AffinityExecutor, val database: Database) : ArtemisMessagingComponent(), MessagingServiceInternal { companion object { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt index 6879491ac2..469bd3ce16 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCStructures.kt @@ -9,10 +9,15 @@ import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.serializers.JavaSerializer 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.core.ErrorOr 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.PhysicalLocation 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.WireTransaction 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.EdDSAPublicKey import org.objenesis.strategy.StdInstantiatorStrategy @@ -148,6 +150,8 @@ private class RPCKryo(observableSerializer: Serializer>? = null) register(ByteArray::class.java) register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer) register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer) + register(PublicKeyTree.Leaf::class.java) + register(PublicKeyTree.Node::class.java) register(Vault::class.java) register(Vault.Update::class.java) register(StateMachineRunId::class.java) @@ -179,7 +183,7 @@ private class RPCKryo(observableSerializer: Serializer>? = null) register(ArtemisMessagingComponent.NodeAddress::class.java, read = { kryo, input -> ArtemisMessagingComponent.NodeAddress( - parsePublicKeyBase58(kryo.readObject(input, String::class.java)), + PublicKeyTree.parseFromBase58(kryo.readObject(input, String::class.java)), kryo.readObject(input, HostAndPort::class.java)) }, write = { kryo, output, nodeAddress -> diff --git a/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt index 97b9d63a47..3afbea5389 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/InMemoryNetworkMapCache.kt @@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture import net.corda.core.bufferUntilSubscribed import net.corda.core.contracts.Contract 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.messaging.MessagingService import net.corda.core.messaging.SingleMessageRecipient @@ -29,7 +29,6 @@ import net.corda.node.utilities.AddOrRemove import net.corda.protocols.sendRequest import rx.Observable import rx.subjects.PublishSubject -import java.security.PublicKey import java.security.SignatureException import java.util.* 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 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 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. val candidates = get().filter { (it.legalIdentity.owningKey == publicKey) || it.advertisedServices.any { it.identity.owningKey == publicKey } } 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() } diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index 6eccdaadd1..d3244225c7 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -1,6 +1,7 @@ package net.corda.node.services.network import com.google.common.annotations.VisibleForTesting +import kotlinx.support.jdk8.collections.compute import net.corda.core.ThreadBox import net.corda.core.crypto.DigitalSignature 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.utilities.AddOrRemove import net.corda.protocols.ServiceRequestMessage -import kotlinx.support.jdk8.collections.compute import java.security.PrivateKey import java.security.SignatureException import java.time.Instant @@ -333,7 +333,7 @@ class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemo */ fun toWire(privateKey: PrivateKey): WireNodeRegistration { 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) } @@ -347,7 +347,7 @@ class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemo class WireNodeRegistration(raw: SerializedBytes, sig: DigitalSignature.WithKey) : SignedData(raw, sig) { @Throws(IllegalArgumentException::class) override fun verifyData(data: NodeRegistration) { - require(data.node.legalIdentity.owningKey == sig.by) + require(data.node.legalIdentity.owningKey.isFulfilledBy(sig.by)) } } diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index d820647f73..9db56aed6b 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -6,6 +6,7 @@ import net.corda.core.ThreadBox import net.corda.core.bufferUntilSubscribed import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.node.ServiceHub import net.corda.core.node.services.Vault @@ -155,8 +156,8 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT */ override fun generateSpend(tx: TransactionBuilder, amount: Amount, - to: PublicKey, - onlyFromParties: Set?): Pair> { + to: PublicKeyTree, + onlyFromParties: Set?): Pair> { // Discussion // // 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) } - private fun deriveState(txState: TransactionState, amount: Amount>, owner: PublicKey) + private fun deriveState(txState: TransactionState, amount: Amount>, owner: PublicKeyTree) = 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): Boolean { return if (state is OwnableState) { - state.owner in ourKeys + state.owner.containsAny(ourKeys) } else if (state is LinearState) { // It's potentially of interest to the vault state.isRelevant(ourKeys) diff --git a/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt b/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt index dddfa93560..551aad576b 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/DatabaseSupport.kt @@ -1,11 +1,12 @@ package net.corda.node.utilities 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.parsePublicKeyBase58 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.transactions.TransactionInterface import org.jetbrains.exposed.sql.transactions.TransactionManager @@ -137,15 +138,17 @@ class StrandLocalTransactionManager(initWithDatabase: Database) : TransactionMan } // Composite columns for use with below Exposed helpers. -data class PartyColumns(val name: Column, val owningKey: Column) +data class PartyColumns(val name: Column, val owningKey: Column) data class StateRefColumns(val txId: Column, val index: Column) /** * [Table] column helpers for use with Exposed, as per [varchar] etc. */ fun Table.publicKey(name: String) = this.registerColumn(name, PublicKeyColumnType) + +fun Table.publicKeyTree(name: String) = this.registerColumn(name, PublicKeyTreeColumnType) fun Table.secureHash(name: String) = this.registerColumn(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(name, UUIDStringColumnType) fun Table.localDate(name: String) = this.registerColumn(name, LocalDateColumnType) fun Table.localDateTime(name: String) = this.registerColumn(name, LocalDateTimeColumnType) @@ -163,6 +166,15 @@ object PublicKeyColumnType : ColumnType() { 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]. */ diff --git a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt index 069399b904..f2f916ef0b 100644 --- a/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt +++ b/node/src/test/kotlin/net/corda/node/CordaRPCOpsImplTest.kt @@ -146,8 +146,9 @@ class CordaRPCOpsImplTest { require(tx.tx.outputs.size == 1) val signaturePubKeys = tx.sigs.map { it.by }.toSet() // Only Alice signed - require(signaturePubKeys.size == 1) - require(signaturePubKeys.contains(aliceNode.info.legalIdentity.owningKey)) + val aliceKey = aliceNode.info.legalIdentity.owningKey + require(signaturePubKeys.size <= aliceKey.keys.size) + require(aliceKey.isFulfilledBy(signaturePubKeys)) }, // MOVE expect { tx -> @@ -155,9 +156,8 @@ class CordaRPCOpsImplTest { require(tx.tx.outputs.size == 1) val signaturePubKeys = tx.sigs.map { it.by }.toSet() // Alice and Notary signed - require(signaturePubKeys.size == 2) - require(signaturePubKeys.contains(aliceNode.info.legalIdentity.owningKey)) - require(signaturePubKeys.contains(notaryNode.info.notaryIdentity.owningKey)) + require(aliceNode.info.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys)) + require(notaryNode.info.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys)) } ) } diff --git a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeProtocolTests.kt b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeProtocolTests.kt index 083d028dfe..26bb3e8e7e 100644 --- a/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeProtocolTests.kt +++ b/node/src/test/kotlin/net/corda/node/messaging/TwoPartyTradeProtocolTests.kt @@ -5,7 +5,9 @@ import net.corda.contracts.asset.* import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash +import net.corda.core.crypto.tree import net.corda.core.days import net.corda.core.map import net.corda.core.messaging.SingleMessageRecipient @@ -40,7 +42,6 @@ import rx.Observable import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.security.KeyPair -import java.security.PublicKey import java.util.* import java.util.concurrent.ExecutionException import java.util.concurrent.Future @@ -250,7 +251,7 @@ class TwoPartyTradeProtocolTests { } 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 alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, 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 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) val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey, 1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second @@ -433,7 +434,7 @@ class TwoPartyTradeProtocolTests { val bobKey = bobNode.services.legalIdentityKey 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, 1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second @@ -480,7 +481,7 @@ class TwoPartyTradeProtocolTests { private fun LedgerDSL.fillUpForBuyer( withError: Boolean, - owner: PublicKey = BOB_PUBKEY, + owner: PublicKeyTree = BOB_PUBKEY, notary: Party): Pair> { val issuer = DUMMY_CASH_ISSUER // 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 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } if (!withError) - command(DUMMY_CASH_ISSUER_KEY.public) { Cash.Commands.Issue() } + command(DUMMY_CASH_ISSUER_KEY.public.tree) { Cash.Commands.Issue() } else // 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) if (withError) { this.fails() @@ -524,7 +525,7 @@ class TwoPartyTradeProtocolTests { private fun LedgerDSL.fillUpForSeller( withError: Boolean, - owner: PublicKey, + owner: PublicKeyTree, amount: Amount>, attachmentID: SecureHash?, notary: Party): Pair> { diff --git a/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt index 2256e7fac1..a7a1c9bdb5 100644 --- a/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/ArtemisMessagingTests.kt @@ -1,12 +1,8 @@ package net.corda.node.services 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.tree import net.corda.core.messaging.Message import net.corda.core.messaging.createMessage import net.corda.core.node.services.DEFAULT_SESSION_ID @@ -147,7 +143,7 @@ class ArtemisMessagingTests { private fun createMessagingClient(server: HostAndPort = hostAndPort): NodeMessagingClient { 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() messagingClient = this } diff --git a/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt b/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt index 4764994b08..9f5d0aea09 100644 --- a/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/InMemoryNetworkMapCacheTest.kt @@ -1,9 +1,9 @@ package net.corda.node.services import net.corda.core.crypto.generateKeyPair +import net.corda.core.crypto.tree import net.corda.core.node.services.ServiceInfo import net.corda.node.services.network.NetworkMapService -import net.corda.node.services.transactions.SimpleNotaryService import net.corda.testing.expect import net.corda.testing.node.MockNetwork 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)) // 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) // Now both nodes match, so it throws an error expect { - nodeA.netMapCache.getNodeByPublicKey(keyPair.public) + nodeA.netMapCache.getNodeByPublicKey(keyPair.public.tree) } } } diff --git a/node/src/test/kotlin/net/corda/node/services/NodeSchedulerServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/NodeSchedulerServiceTest.kt index 3b62083847..4c0454d05f 100644 --- a/node/src/test/kotlin/net/corda/node/services/NodeSchedulerServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/NodeSchedulerServiceTest.kt @@ -1,6 +1,8 @@ package net.corda.node.services 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.node.ServiceHub import net.corda.core.node.recordTransactions @@ -109,7 +111,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { } class TestState(val protocolLogicRef: ProtocolLogicRef, val instant: Instant) : LinearState, SchedulableState { - override val participants: List + override val participants: List get() = throw UnsupportedOperationException() override val linearId = UniqueIdentifier() @@ -268,7 +270,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() { val state = TestState(factory.create(TestProtocolLogic::class.java, increment), instant) val usefulTX = TransactionType.General.Builder(null).apply { addOutputState(state, DUMMY_NOTARY) - addCommand(Command(), freshKey.public) + addCommand(Command(), freshKey.public.tree) signWith(freshKey) }.toSignedTransaction() val txHash = usefulTX.id diff --git a/node/src/test/kotlin/net/corda/node/services/NodeVaultServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/NodeVaultServiceTest.kt index 071d4a3f39..d322b7db87 100644 --- a/node/src/test/kotlin/net/corda/node/services/NodeVaultServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/NodeVaultServiceTest.kt @@ -6,6 +6,7 @@ import net.corda.core.contracts.DOLLARS import net.corda.core.contracts.POUNDS import net.corda.core.contracts.TransactionType 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.VaultService import net.corda.core.transactions.SignedTransaction @@ -102,7 +103,7 @@ class NodeVaultServiceTest { // Issue a txn to Send us some Money 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) }.toSignedTransaction() @@ -116,7 +117,7 @@ class NodeVaultServiceTest { // Issue more Money (GBP) 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) }.toSignedTransaction() diff --git a/node/src/test/kotlin/net/corda/node/services/ValidatingNotaryServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/ValidatingNotaryServiceTests.kt index 778a2e94e9..161d8ce8ca 100644 --- a/node/src/test/kotlin/net/corda/node/services/ValidatingNotaryServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/ValidatingNotaryServiceTests.kt @@ -3,6 +3,7 @@ package net.corda.node.services import com.google.common.util.concurrent.ListenableFuture import net.corda.core.contracts.* import net.corda.core.crypto.DigitalSignature +import net.corda.core.crypto.tree import net.corda.core.node.services.ServiceInfo import net.corda.core.transactions.SignedTransaction import net.corda.core.utilities.DUMMY_NOTARY @@ -56,7 +57,7 @@ class ValidatingNotaryServiceTests { } @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 inputState = issueState(clientNode) diff --git a/node/src/test/kotlin/net/corda/node/services/VaultWithCashTest.kt b/node/src/test/kotlin/net/corda/node/services/VaultWithCashTest.kt index fec2c54f39..917a5051a5 100644 --- a/node/src/test/kotlin/net/corda/node/services/VaultWithCashTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/VaultWithCashTest.kt @@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash import net.corda.contracts.asset.DUMMY_CASH_ISSUER import net.corda.contracts.testing.fillWithSomeTestCash import net.corda.core.contracts.* +import net.corda.core.crypto.tree import net.corda.core.node.recordTransactions import net.corda.core.node.services.VaultService import net.corda.core.transactions.SignedTransaction @@ -72,7 +73,7 @@ class VaultWithCashTest { val state = w.states.toList()[0].state.data as Cash.State 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.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. val freshKey = services.keyManagementService.freshKey() 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) }.toSignedTransaction() @@ -103,7 +104,7 @@ class VaultWithCashTest { // A tx that doesn't send us anything. 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(DUMMY_NOTARY_KEY) }.toSignedTransaction() @@ -127,8 +128,8 @@ class VaultWithCashTest { // Issue a linear state 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))) + addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree))) + addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree))) signWith(freshKey) signWith(DUMMY_NOTARY_KEY) }.toSignedTransaction() @@ -148,7 +149,7 @@ class VaultWithCashTest { // Issue a linear state 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(DUMMY_NOTARY_KEY) }.toSignedTransaction() @@ -160,7 +161,7 @@ class VaultWithCashTest { // Move the same state 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(0)) signWith(DUMMY_NOTARY_KEY) }.toSignedTransaction() diff --git a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt index b002833c41..5213963759 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/CoreTestUtils.kt @@ -6,10 +6,9 @@ import com.google.common.base.Throwables import com.google.common.net.HostAndPort import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture +import com.typesafe.config.Config import net.corda.core.contracts.StateRef -import net.corda.core.crypto.Party -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.generateKeyPair +import net.corda.core.crypto.* import net.corda.core.node.ServiceHub import net.corda.core.protocols.ProtocolLogic 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.testing.node.MockIdentityService import net.corda.testing.node.MockServices -import com.typesafe.config.Config import rx.Subscriber import java.net.ServerSocket import java.security.KeyPair -import java.security.PublicKey import kotlin.reflect.KClass /** @@ -48,24 +45,24 @@ import kotlin.reflect.KClass // A few dummy values for testing. 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_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_PUBKEY: PublicKey get() = ORACLE_KEY.public +val ORACLE_PUBKEY: PublicKeyTree get() = ORACLE_KEY.public.tree 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 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 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 MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY) diff --git a/test-utils/src/main/kotlin/net/corda/testing/DummyLinearState.kt b/test-utils/src/main/kotlin/net/corda/testing/DummyLinearState.kt index 7fd0da0032..d34282c74c 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/DummyLinearState.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/DummyLinearState.kt @@ -4,6 +4,7 @@ import net.corda.core.contracts.* import net.corda.core.contracts.clauses.Clause import net.corda.core.contracts.clauses.FilterOn import net.corda.core.contracts.clauses.verifyClause +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import java.security.PublicKey @@ -18,11 +19,11 @@ class DummyLinearContract: Contract { class State( override val linearId: UniqueIdentifier = UniqueIdentifier(), override val contract: Contract = DummyLinearContract(), - override val participants: List = listOf(), + override val participants: List = listOf(), val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState { override fun isRelevant(ourKeys: Set): Boolean { - return participants.any { ourKeys.contains(it) } + return participants.any { it.containsAny(ourKeys) } } } } diff --git a/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt b/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt index b93a0cccca..38c7491873 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/TestDSL.kt @@ -9,7 +9,6 @@ import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.WireTransaction import net.corda.core.utilities.DUMMY_NOTARY_KEY -import net.corda.core.crypto.* import java.io.InputStream import java.security.KeyPair import java.security.PublicKey @@ -130,7 +129,7 @@ data class TestTransactionDSLInterpreter private constructor( transactionBuilder.addAttachment(attachmentId) } - override fun _command(signers: List, commandData: CommandData) { + override fun _command(signers: List, commandData: CommandData) { val command = Command(commandData, signers) transactionBuilder.addCommand(command) } @@ -325,7 +324,7 @@ fun signAll(transactionsToSign: List, extraKeys: List) (ALL_TEST_KEYS + extraKeys).forEach { keyLookup[it.public] = it } - wtx.mustSign.forEach { + wtx.mustSign.keys.forEach { val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}") signatures += key.signWithECDSA(wtx.id) } diff --git a/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt b/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt index d521431696..9c70fb475f 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/TransactionDSLInterpreter.kt @@ -2,11 +2,11 @@ package net.corda.testing import net.corda.core.contracts.* import net.corda.core.crypto.Party +import net.corda.core.crypto.PublicKeyTree import net.corda.core.crypto.SecureHash import net.corda.core.seconds import net.corda.core.transactions.TransactionBuilder import net.corda.core.utilities.DUMMY_NOTARY -import java.security.PublicKey import java.time.Duration import java.time.Instant @@ -46,7 +46,7 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup { * @param signers The signer public keys. * @param commandData The contents of the command. */ - fun _command(signers: List, commandData: CommandData) + fun _command(signers: List, commandData: CommandData) /** * Adds a timestamp to the transaction. @@ -99,12 +99,12 @@ class TransactionDSL(val interpreter: T) : Tr /** * @see TransactionDSLInterpreter._command */ - fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) = + fun command(vararg signers: PublicKeyTree, commandDataClosure: () -> CommandData) = _command(listOf(*signers), commandDataClosure()) /** * @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. diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt index ac5a1c98e6..9bbbb4df57 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -2,10 +2,7 @@ package net.corda.testing.node import com.google.common.util.concurrent.ListenableFuture import net.corda.core.contracts.Attachment -import net.corda.core.crypto.Party -import net.corda.core.crypto.SecureHash -import net.corda.core.crypto.generateKeyPair -import net.corda.core.crypto.sha256 +import net.corda.core.crypto.* import net.corda.core.messaging.MessagingService import net.corda.core.messaging.SingleMessageRecipient 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 clock: Clock 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 class MockIdentityService(val identities: List) : IdentityService, SingletonSerializeAsToken() { - private val keyToParties: Map + private val keyToParties: Map get() = synchronized(identities) { identities.associateBy { it.owningKey } } private val nameToParties: Map get() = synchronized(identities) { identities.associateBy { it.name } } 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] } diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt index 1ff3080ebe..ea2efc4fc3 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/views/TransactionViewer.kt @@ -1,20 +1,5 @@ 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.value.ObservableValue import javafx.collections.FXCollections @@ -29,8 +14,23 @@ import javafx.scene.layout.BackgroundFill import javafx.scene.layout.BorderPane import javafx.scene.layout.CornerRadii 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 java.security.PublicKey import java.util.* class TransactionViewer : View() { @@ -136,7 +136,7 @@ class TransactionViewer : View() { override val root: Parent by fxml() private val inputs: ListView by fxid() private val outputs: ListView by fxid() - private val signatures: ListView by fxid() + private val signatures: ListView by fxid() private val inputPane: TitledPane by fxid() private val outputPane: 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) } - val signatureData = transaction.transaction.sigs.map { it.by } + val signatureData = transaction.transaction.sigs.map { it.by.tree } // Bind count to TitlePane inputPane.textProperty().bind(inputStates.lift().map { "Input (${it.count()})" }) outputPane.textProperty().bind(outputStates.lift().map { "Output (${it.count()})" }) @@ -172,14 +172,14 @@ class TransactionViewer : View() { } field("Issuer :") { label("${data.amount.token.issuer}") { - tooltip(data.amount.token.issuer.party.owningKey.toStringShort()) + tooltip(data.amount.token.issuer.party.owningKey.toString()) } } field("Owner :") { val owner = data.owner val nodeInfo = Models.get(TransactionViewer::class).lookup(owner) label(nodeInfo?.legalIdentity?.name ?: "???") { - tooltip(data.owner.toStringShort()) + tooltip(data.owner.toString()) } } } @@ -201,7 +201,7 @@ class TransactionViewer : View() { signatures.apply { cellFormat { key -> val nodeInfo = Models.get(TransactionViewer::class).lookup(key) - text = "${key.toStringShort()} (${nodeInfo?.legalIdentity?.name ?: "???"})" + text = "$key (${nodeInfo?.legalIdentity?.name ?: "???"})" } prefHeight = 185.0 }