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

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

View File

@ -174,8 +174,9 @@ class NodeMonitorModelTest {
require(tx.tx.outputs.size == 1)
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
}
)

View File

@ -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 }
}
}

View File

@ -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 <R> requireThat(body: Requirements.() -> R) = R.body()
//// Authenticated commands ///////////////////////////////////////////////////////////////////////////////////////////
/** Filters the command list by type, party and public key all at once. */
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signer: PublicKey? = null,
party: Party? = null) =
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.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 <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signers: Collection<PublicKey>?,
parties: Collection<Party>?) =
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signers: Collection<PublicKeyTree>?,
parties: Collection<Party>?) =
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) }.

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
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<PublicKey>) : ContractState, State {
val owners: List<PublicKeyTree>) : ContractState, State {
override val contract = DUMMY_PROGRAM_ID
override val participants: List<PublicKey>
override val participants: List<PublicKeyTree>
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<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder {
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKeyTree) = move(listOf(prior), newOwner)
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKeyTree): TransactionBuilder {
require(priors.size > 0)
val priorState = priors[0].state.data
val (cmd, state) = priorState.withNewOwner(newOwner)

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
get() = emptyList()
}

View File

@ -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<T> : 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<PublicKey>
val exitKeys: Collection<PublicKeyTree>
/** There must be a MoveCommand signed by this key to claim the amount */
override val owner: PublicKey
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKey): FungibleAsset<T>
override val owner: PublicKeyTree
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKeyTree): FungibleAsset<T>
// Just for grouping
interface Commands : CommandData {

View File

@ -2,8 +2,8 @@ package net.corda.core.contracts
import net.corda.core.contracts.clauses.Clause
import net.corda.core.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<PublicKey>
val participants: List<PublicKeyTree>
/**
* All contract states may be _encumbered_ by up to one other state.
@ -184,10 +184,10 @@ fun <T> Amount<Issued<T>>.withoutIssuer(): Amount<T> = Amount(quantity, token.pr
*/
interface OwnableState : ContractState {
/** 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<CommandData, OwnableState>
fun withNewOwner(newOwner: PublicKeyTree): Pair<CommandData, OwnableState>
}
/** 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<PublicKey>) {
data class Command(val value: CommandData, val signers: List<PublicKeyTree>) {
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<out T : Any>(
val signers: List<PublicKey>,
val signers: List<PublicKeyTree>,
/** If any public keys were recognised, the looked up institutions are available here */
val signingParties: List<Party>,
val value: T

View File

@ -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<PublicKey> {
fun verifySigners(tx: LedgerTransaction): Set<PublicKeyTree> {
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<PublicKey>
abstract fun getRequiredSigners(tx: LedgerTransaction): Set<PublicKeyTree>
/** Implement type specific transaction validation logic */
abstract fun verifyTransaction(tx: LedgerTransaction)

View File

@ -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<PublicKey>) : TransactionVerificationException(tx, null) {
override fun toString() = "Signers missing: ${missing.map { it.toStringShort() }}"
class SignersMissing(tx: LedgerTransaction, val missing: List<PublicKeyTree>) : 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) {

View File

@ -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<PublicKey> {
@ -42,6 +43,8 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
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<PublicKey> {
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<PublicKey>.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)

View File

@ -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))
}
}

View File

@ -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<PublicKey>
abstract val keys: Set<PublicKey>
/** Checks whether any of the given [keys] matches a leaf on the tree */
fun containsAny(keys: Iterable<PublicKey>) = getKeys().intersect(keys).isNotEmpty()
fun containsAny(otherKeys: Iterable<PublicKey>) = keys.intersect(otherKeys).isNotEmpty()
// TODO: implement a proper encoding/decoding mechanism
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>) = publicKey in keys
override fun getKeys(): Set<PublicKey> = setOf(publicKey)
override val keys: Set<PublicKey>
get() = setOf(publicKey)
// Auto-generated. TODO: remove once data class inheritance is enabled
// TODO: remove once data class inheritance is enabled
override fun equals(other: Any?): Boolean {
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<PublicKey> = children.flatMap { it.getKeys() }.toSet()
override val keys: Set<PublicKey>
get() = children.flatMap { it.keys }.toSet()
// Auto-generated. TODO: remove once data class inheritance is enabled
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<PublicKey>): Builder = addLeaves(*publicKeys.toTypedArray())
fun addLeaves(vararg publicKeys: PublicKey) = addKeys(*publicKeys.map { it.tree }.toTypedArray())
fun addKeys(publicKeys: List<PublicKeyTree>): 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<PublicKeyTree>.getKeys() = flatMap { it.getKeys() }.toSet()
val Iterable<PublicKeyTree>.keys: Set<PublicKey>
get() = flatMap { it.keys }.toSet()

View File

@ -1,13 +1,13 @@
package net.corda.core.node
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)
}
/**

View File

@ -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?
}

View File

@ -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

View File

@ -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<Currency>,
to: PublicKey,
onlyFromParties: Set<Party>? = null): Pair<TransactionBuilder, List<PublicKey>>
to: PublicKeyTree,
onlyFromParties: Set<Party>? = null): Pair<TransactionBuilder, List<PublicKeyTree>>
}
inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java)
@ -201,14 +203,19 @@ inline fun <reified T : DealState> VaultService.dealsWith(party: Party) = linear
* The current interface is obviously not usable for those use cases: this is just where we'd put a real signing
* interface if/when one is developed.
*/
interface KeyManagementService {
/** Returns a snapshot of the current pubkey->privkey mapping. */
val keys: Map<PublicKey, PrivateKey>
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key")
// TODO: make toPrivate return null if not found instead of throwing
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key ${publicKey.toStringShort()}")
fun toKeyPair(publicKey: PublicKey) = KeyPair(publicKey, toPrivate(publicKey))
/** Returns the first [KeyPair] matching any of the [publicKeys] */
fun toKeyPair(publicKeys: Iterable<PublicKey>) = publicKeys.first { keys.contains(it) }.let { toKeyPair(it) }
/** Generates a new random key and adds it to the exposed map. */
fun freshKey(): KeyPair
}

View File

@ -10,6 +10,8 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.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<WireTransaction>() {
val outputs = kryo.readClassAndObject(input) as List<TransactionState<ContractState>>
val commands = kryo.readClassAndObject(input) as List<Command>
val notary = kryo.readClassAndObject(input) as Party?
val signers = kryo.readClassAndObject(input) as List<PublicKey>
val signers = kryo.readClassAndObject(input) as List<PublicKeyTree>
val transactionType = kryo.readClassAndObject(input) as TransactionType
val timestamp = kryo.readClassAndObject(input) as Timestamp?

View File

@ -6,9 +6,7 @@ import com.pholser.junit.quickcheck.generator.java.lang.StringGenerator
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
import com.pholser.junit.quickcheck.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>(PublicKey::class.java) {
}
}
class PublicKeyTreeGenerator : Generator<PublicKeyTree>(PublicKeyTree::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PublicKeyTree {
return entropyToKeyPair(random.nextBigInteger(32)).public.tree
}
}
class PartyGenerator: Generator<Party>(Party::class.java) {
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Party {
return Party(StringGenerator().generate(random, status), PublicKeyGenerator().generate(random, status))
return Party(StringGenerator().generate(random, status), PublicKeyTreeGenerator().generate(random, status))
}
}

View File

@ -2,7 +2,7 @@ package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.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<PublicKey>,
val mustSign: List<PublicKeyTree>,
/**
* Pointer to a class that defines the behaviour of this transaction: either normal, or "notary changing".
*/

View File

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

View File

@ -3,12 +3,11 @@ package net.corda.core.transactions
import net.corda.core.contracts.NamedByHash
import net.corda.core.contracts.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<WireTransaction>,
/** Lazily calculated access to the deserialised/hashed transaction data. */
val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) }
class SignaturesMissingException(val missing: Set<PublicKey>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() {
class SignaturesMissingException(val missing: Set<PublicKeyTree>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() {
override fun toString(): String {
return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.toStringsShort()}"
return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}"
}
}
@ -53,7 +52,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
* @throws SignaturesMissingException if any signatures should have been present but were not.
*/
@Throws(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<WireTransaction>,
}
}
private fun getMissingSignatures(): Set<PublicKey> {
val requiredKeys = tx.mustSign.toSet()
private fun getMissingSignatures(): Set<PublicKeyTree> {
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<PublicKey>): ArrayList<String> {
private fun getMissingKeyDescriptions(missing: Set<PublicKeyTree>): ArrayList<String> {
// TODO: We need a much better way of structuring this data
val missingElements = ArrayList<String>()
this.tx.commands.forEach { command ->

View File

@ -3,11 +3,7 @@ package net.corda.core.transactions
import net.corda.core.contracts.*
import net.corda.core.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<SecureHash> = arrayListOf(),
protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(),
protected val commands: MutableList<Command> = arrayListOf(),
protected val signers: MutableSet<PublicKey> = mutableSetOf(),
protected val signers: MutableSet<PublicKeyTree> = 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<PublicKey> = signers - gotKeys
val missing: Set<PublicKeyTree> = 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<PublicKey>) = addCommand(Command(data, keys))
fun addCommand(data: CommandData, vararg keys: PublicKeyTree) = addCommand(Command(data, listOf(*keys)))
fun addCommand(data: CommandData, keys: List<PublicKeyTree>) = addCommand(Command(data, keys))
// Accessors that yield immutable snapshots.
fun inputStates(): List<StateRef> = ArrayList(inputs)

View File

@ -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<Command>,
notary: Party?,
signers: List<PublicKey>,
signers: List<PublicKeyTree>,
type: TransactionType,
timestamp: Timestamp?
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {

View File

@ -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) {

View File

@ -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() }

View File

@ -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<T> {
}
abstract protected fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal<T>
abstract protected fun assembleTx(): Pair<SignedTransaction, List<PublicKey>>
abstract protected fun assembleTx(): Pair<SignedTransaction, List<PublicKeyTree>>
@Suspendable
private fun collectSignatures(participants: List<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
private fun collectSignatures(participants: List<PublicKeyTree>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
val parties = participants.map {
val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(it) ?:
throw IllegalStateException("Participant $it to state $originalState not found on the network")
@ -95,7 +95,7 @@ abstract class AbstractStateReplacementProtocol<T> {
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
}

View File

@ -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)
}
}

View File

@ -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<Party>() {
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal<Party>
= Proposal(stateRef, modification, stx)
override fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> {
override fun assembleTx(): Pair<SignedTransaction, List<PublicKeyTree>> {
val state = originalState.state
val newState = state.withNotary(modification)
val participants = state.data.participants

View File

@ -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<PublicKey>) : NotaryError()
class SignaturesMissing(val missingSigners: Set<PublicKeyTree>) : NotaryError()
}

View File

@ -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<out T>(val payload: T, val publicKey: PublicKey)
data class Handshake<out T>(val payload: T, val publicKey: PublicKeyTree)
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.LegallyIdentifiable)
@ -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<SignedTransaction>(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<SignaturesFromPrimary>(otherParty, stx).unwrap { it }
}
private fun signWithOurKeys(signingPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
private fun signWithOurKeys(signingPubKeys: List<PublicKeyTree>, ptx: TransactionBuilder): SignedTransaction {
// Now sign the transaction with whatever keys we need to move the cash.
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<U>): Handshake<U>
@Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<PublicKey>>
@Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<PublicKeyTree>>
}
@ -295,7 +292,7 @@ object TwoPartyDealProtocol {
return handshake.copy(payload = autoOffer.copy(dealBeingOffered = deal))
}
override fun assembleSharedTX(handshake: Handshake<AutoOffer>): Pair<TransactionBuilder, List<PublicKey>> {
override fun assembleSharedTX(handshake: Handshake<AutoOffer>): Pair<TransactionBuilder, List<PublicKeyTree>> {
val deal = handshake.payload.dealBeingOffered
val ptx = deal.generateAgreement(handshake.payload.notary)

View File

@ -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<PublicKey> = emptyList()
override val participants: List<PublicKeyTree> = emptyList()
override val contract: Contract = TEST_TIMELOCK_ID
}
}

View File

@ -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)

View File

@ -4,6 +4,7 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.core.crypto.Party
import net.corda.core.crypto.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<IllegalArgumentException> { make().verifySignatures() }
assertEquals(
setOf(DUMMY_KEY_1.public),
setOf(DUMMY_KEY_1.public.tree),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_2).verifySignatures() }.missing
)
assertEquals(
setOf(DUMMY_KEY_2.public),
setOf(DUMMY_KEY_2.public.tree),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_1).verifySignatures() }.missing
)
assertEquals(
setOf(DUMMY_KEY_2.public),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public) }.missing
setOf(DUMMY_KEY_2.public.tree),
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public.tree) }.missing
)
make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public)
make(DUMMY_KEY_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<AuthenticatedObject<CommandData>>()
val attachments = emptyList<Attachment>()
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<AuthenticatedObject<CommandData>>()
val attachments = emptyList<Attachment>()
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,

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
get() = listOf()
}

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
get() = emptyList()
override val contract = VaultUpdateTests.DummyContract
}

View File

@ -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<Currency>,
override val owner: PublicKey) : OwnableState {
override val owner: PublicKeyTree) : OwnableState {
override val contract: Contract = TEST_PROGRAM_ID
override val participants: List<PublicKey>
override val participants: List<PublicKeyTree>
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)

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
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))
}

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
get() = listOf(owner)
override fun withNewOwner(newOwner: PublicKey): Pair<CommandData, OwnableState> {
override fun withNewOwner(newOwner: PublicKeyTree): Pair<CommandData, OwnableState> {
return Pair(Commands.TransferPossession(), copy(owner = newOwner))
}
@ -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<State>, newOwner: PublicKey, newBeneficiary: Party) {
fun generateTransferAndEndorse(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKeyTree, newBeneficiary: Party) {
tx.addInputState(BoL)
tx.addOutputState(BoL.state.data.copy(owner = newOwner, beneficiary = newBeneficiary))
val signers: List<PublicKey> = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey)
val signers: List<PublicKeyTree> = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey)
tx.addCommand(Commands.TransferAndEndorseBL(), signers)
}
/**
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
*/
fun generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKey) {
fun generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKeyTree) {
tx.addInputState(BoL)
tx.addOutputState(BoL.state.data.copy(owner = newOwner))
// tx.addOutputState(BoL.state.data.copy().withNewOwner(newOwner))

View File

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

View File

@ -1,11 +1,11 @@
package net.corda.contracts
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<PublicKey>
override val participants: List<PublicKeyTree>
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 {

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
get() = listOf()
}

View File

@ -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<PublicKey>,
data class State(override val participants: List<PublicKeyTree>,
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)

View File

@ -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<PublicKey> =
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKeyTree> =
when (arrangement) {
is Zero -> ImmutableSet.of<PublicKey>()
is Zero -> ImmutableSet.of<PublicKeyTree>()
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
is And ->
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
arrangement.arrangements.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
is Actions ->
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
arrangement.actions.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
is RollOut -> liablePartiesVisitor(arrangement.template)
is Continuation -> ImmutableSet.of<PublicKey>()
is Continuation -> ImmutableSet.of<PublicKeyTree>()
else -> throw IllegalArgumentException("liableParties " + arrangement)
}
private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKey> =
private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKeyTree> =
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<PublicKey> = liablePartiesVisitor(contract)
fun liableParties(contract: Arrangement): Set<PublicKeyTree> = liablePartiesVisitor(contract)
private fun involvedPartiesVisitor(action: Action): Set<PublicKey> =
private fun involvedPartiesVisitor(action: Action): Set<PublicKeyTree> =
Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy()
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> =
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKeyTree> =
when (arrangement) {
is Zero -> ImmutableSet.of<PublicKey>()
is Zero -> ImmutableSet.of<PublicKeyTree>()
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
is And ->
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
arrangement.arrangements.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
is Actions ->
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
arrangement.actions.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
else -> throw IllegalArgumentException()
}
/** returns list of involved parties for a given contract */
fun involvedParties(arrangement: Arrangement): Set<PublicKey> = involvedPartiesVisitor(arrangement)
fun involvedParties(arrangement: Arrangement): Set<PublicKeyTree> = involvedPartiesVisitor(arrangement)
fun replaceParty(action: Action, from: Party, to: Party): Action =
if (action.actors.contains(from)) {

View File

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

View File

@ -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);

View File

@ -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<Issued<Currency>> faceValue;
private Instant maturityDate;
public State() {
} // For serialization
public State(PartyAndReference issuance, PublicKey owner, Amount<Issued<Currency>> faceValue,
public State(PartyAndReference issuance, PublicKeyTree owner, Amount<Issued<Currency>> 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<CommandData, OwnableState> withNewOwner(@NotNull PublicKey newOwner) {
public Pair<CommandData, OwnableState> withNewOwner(@NotNull PublicKeyTree newOwner) {
return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
}
@ -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<PublicKey> getParticipants() {
public List<PublicKeyTree> 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<State> paper, PublicKey newOwner) {
public void generateMove(TransactionBuilder tx, StateAndRef<State> 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()));

View File

@ -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<Issued<Currency>>,
val maturityDate: Instant
) : OwnableState, QueryableState, ICommercialPaperState {
override val contract = CP_PROGRAM_ID
override val participants: List<PublicKey>
override val participants: List<PublicKeyTree>
get() = listOf(owner)
val token: Issued<Terms>
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<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
@ -205,7 +200,7 @@ class CommercialPaper : Contract {
/**
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
*/
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, 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)

View File

@ -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<Issued<Currency>>,
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<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
@ -119,7 +115,7 @@ class CommercialPaperLegacy : Contract {
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
}
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKeyTree) {
tx.addInputState(paper)
tx.addOutputState(paper.state.data.withOwner(newOwner))
tx.addCommand(Command(Commands.Move(), paper.state.data.owner))

View File

@ -9,16 +9,13 @@ import net.corda.core.contracts.clauses.FirstComposition
import net.corda.core.contracts.clauses.GroupClauseVerifier
import net.corda.core.contracts.clauses.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<Currency, Cash.Commands, Cash.State>() {
override val amount: Amount<Issued<Currency>>,
/** 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<Currency>, QueryableState {
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKey)
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKeyTree)
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
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<Issued<Currency>>, newOwner: PublicKey): FungibleAsset<Currency>
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKeyTree): FungibleAsset<Currency>
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
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<Currency, Cash.Commands, Cash.State>() {
/**
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
*/
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKey, notary: Party)
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKeyTree, notary: Party)
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
/**
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKey, notary: Party) {
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKeyTree, notary: Party) {
check(tx.inputStates().isEmpty())
check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
val at = amount.token.issuer
@ -162,7 +159,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
tx.addCommand(generateIssueCommand(), at.party.owningKey)
}
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKeyTree)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount)
override fun generateIssueCommand() = Commands.Issue()
@ -179,7 +176,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
* if there are none, or if any of the cash states cannot be added together (i.e. are
* different currencies or issuers).
*/
fun Iterable<ContractState>.sumCashBy(owner: PublicKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
fun Iterable<ContractState>.sumCashBy(owner: PublicKeyTree): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
/**
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
@ -195,12 +192,12 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
}
fun Cash.State.ownedBy(owner: PublicKey) = copy(owner = owner)
fun Cash.State.ownedBy(owner: PublicKeyTree) = copy(owner = owner)
fun Cash.State.issuedBy(party: Party) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party))))
fun Cash.State.issuedBy(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<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey)
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKeyTree)
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKey)
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKeyTree)

View File

@ -4,15 +4,14 @@ import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.contracts.clause.AbstractIssue
import net.corda.contracts.clause.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<Commodity, CommodityContract.Commands, C
*/
class ConserveAmount : AbstractConserveAmount<State, Commands, Commodity>()
}
/** A state representing a commodity claim against some party */
data class State(
override val amount: Amount<Issued<Commodity>>,
/** There must be a MoveCommand signed by this key to claim the amount */
override val owner: PublicKey
override val owner: PublicKeyTree
) : FungibleAsset<Commodity> {
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKey)
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKeyTree)
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
override val contract = COMMODITY_PROGRAM_ID
override val exitKeys = Collections.singleton(owner)
override val participants = listOf(owner)
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKey): FungibleAsset<Commodity>
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKeyTree): FungibleAsset<Commodity>
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
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<Commodity, CommodityContract.Commands, C
/**
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
*/
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKey, notary: Party)
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKeyTree, notary: Party)
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
/**
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
*/
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKey, notary: Party) {
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKeyTree, notary: Party) {
check(tx.inputStates().isEmpty())
check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
val at = amount.token.issuer
@ -159,7 +158,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
}
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKey)
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKeyTree)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount)
override fun generateIssueCommand() = Commands.Issue()

View File

@ -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<P> : Contract {
val template: Terms<P>,
val quantity: Long,
/** The public key of the entity the contract pays to */
val beneficiary: PublicKey
val beneficiary: PublicKeyTree
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template))
override val contract = OBLIGATION_PROGRAM_ID
override val exitKeys: Collection<PublicKey> = setOf(beneficiary)
override val exitKeys: Collection<PublicKeyTree> = setOf(beneficiary)
val dueBefore: Instant = template.dueBefore
override val participants: List<PublicKey> = listOf(obligor.owningKey, beneficiary)
override val owner: PublicKey = beneficiary
override val participants: List<PublicKeyTree> = listOf(obligor.owningKey, beneficiary)
override val owner: PublicKeyTree = beneficiary
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKey): State<P>
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKeyTree): State<P>
= copy(quantity = newAmount.quantity, beneficiary = newOwner)
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<P>
@ -309,7 +311,7 @@ class Obligation<P> : Contract {
}
}
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(beneficiary = newOwner))
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(beneficiary = newOwner))
}
// Just for grouping
@ -414,7 +416,7 @@ class Obligation<P> : Contract {
* and same parties involved).
*/
fun generateCloseOutNetting(tx: TransactionBuilder,
signer: PublicKey,
signer: PublicKeyTree,
vararg states: State<P>) {
val netState = states.firstOrNull()?.bilateralNetState
@ -442,7 +444,7 @@ class Obligation<P> : Contract {
*/
@Suppress("unused")
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<Terms<P>>>,
assetStates: List<StateAndRef<Obligation.State<P>>>): PublicKey
assetStates: List<StateAndRef<Obligation.State<P>>>): PublicKeyTree
= Clauses.ConserveAmount<P>().generateExit(tx, amountIssued, assetStates,
deriveState = { state, amount, owner -> state.copy(data = state.data.move(amount, owner)) },
generateMoveCommand = { -> Commands.Move() },
@ -456,7 +458,7 @@ class Obligation<P> : Contract {
obligor: Party,
issuanceDef: Terms<P>,
pennies: Long,
beneficiary: PublicKey,
beneficiary: PublicKeyTree,
notary: Party) {
check(tx.inputStates().isEmpty())
check(tx.outputStates().map { it.data }.sumObligationsOrNull<P>() == null)
@ -472,7 +474,7 @@ class Obligation<P> : Contract {
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL })
}
val groups = states.groupBy { it.multilateralNetState }
val partyLookup = HashMap<PublicKey, Party>()
val partyLookup = HashMap<PublicKeyTree, Party>()
val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).toSet()
// Create a lookup table of the party that each public key represents.
@ -516,7 +518,7 @@ class Obligation<P> : Contract {
// Produce a new set of states
val groups = statesAndRefs.groupBy { it.state.data.amount.token }
for ((aggregateState, stateAndRefs) in groups) {
val partiesUsed = ArrayList<PublicKey>()
val partiesUsed = ArrayList<PublicKeyTree>()
stateAndRefs.forEach { stateAndRef ->
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
tx.addInputState(stateAndRef)
@ -561,7 +563,7 @@ class Obligation<P> : Contract {
val template: Terms<P> = issuanceDef.product
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
var obligationRemaining: Amount<P> = obligationTotal
val assetSigners = HashSet<PublicKey>()
val assetSigners = HashSet<PublicKeyTree>()
statesAndRefs.forEach { tx.addInputState(it) }
@ -613,8 +615,8 @@ class Obligation<P> : Contract {
*
* @return a map of obligor/beneficiary pairs to the balance due.
*/
fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>> {
val balances = HashMap<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>>()
fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<Obligation.Terms<P>>> {
val balances = HashMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<Obligation.Terms<P>>>()
states.forEach { state ->
val key = Pair(state.obligor.owningKey, state.beneficiary)
@ -628,8 +630,8 @@ fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligat
/**
* Net off the amounts due between parties.
*/
fun <P> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<Pair<PublicKey, PublicKey>, Amount<P>> {
val nettedBalances = HashMap<Pair<PublicKey, PublicKey>, Amount<P>>()
fun <P> netAmountsDue(balances: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>): Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>> {
val nettedBalances = HashMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>()
balances.forEach { balance ->
val (obligor, beneficiary) = balance.key
@ -653,8 +655,8 @@ fun <P> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map
* @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being
* returned.
*/
fun <P> sumAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<PublicKey, Long> {
val sum = HashMap<PublicKey, Long>()
fun <P> sumAmountsDue(balances: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>): Map<PublicKeyTree, Long> {
val sum = HashMap<PublicKeyTree, Long>()
// Fill the map with zeroes initially
balances.keys.forEach {
@ -695,19 +697,19 @@ fun <P> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<Obligat
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
infix fun <T> Obligation.State<T>.between(parties: Pair<Party, PublicKey>) = copy(obligor = parties.first, beneficiary = parties.second)
infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKey) = copy(beneficiary = owner)
infix fun <T> Obligation.State<T>.between(parties: Pair<Party, PublicKeyTree>) = copy(obligor = parties.first, beneficiary = parties.second)
infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKeyTree) = copy(beneficiary = owner)
infix fun <T> Obligation.State<T>.`issued by`(party: Party) = copy(obligor = party)
// For Java users:
@Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: PublicKey) = copy(beneficiary = owner)
@Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: PublicKeyTree) = copy(beneficiary = owner)
@Suppress("unused") fun <T> Obligation.State<T>.issuedBy(party: Party) = copy(obligor = party)
/** 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<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NullPublicKey)
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NullPublicKeyTree)

View File

@ -2,9 +2,8 @@ package net.corda.contracts.asset
import net.corda.contracts.clause.AbstractConserveAmount
import net.corda.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<T : Any, C: CommandData, S : FungibleAsset<T>> : Co
* @return the public key of the assets issuer, who must sign the transaction for it to be valid.
*/
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
assetStates: List<StateAndRef<S>>): PublicKey
assetStates: List<StateAndRef<S>>): PublicKeyTree
= conserveClause.generateExit(tx, amountIssued, assetStates,
deriveState = { state, amount, owner -> deriveState(state, amount, owner) },
generateMoveCommand = { -> generateMoveCommand() },
@ -56,5 +55,5 @@ abstract class OnLedgerAsset<T : Any, C: CommandData, S : FungibleAsset<T>> : Co
* implementations to have fields in their state which we don't know about here, and we simply leave them untouched
* when sending out "change" from spending/exiting.
*/
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKey): TransactionState<S>
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKeyTree): TransactionState<S>
}

View File

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

View File

@ -6,7 +6,7 @@ import net.corda.contracts.asset.extractAmountsDue
import net.corda.contracts.asset.sumAmountsDue
import net.corda.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<P> {
* Bilateral states are used in close-out netting.
*/
data class BilateralNetState<P>(
val partyKeys: Set<PublicKey>,
val partyKeys: Set<PublicKeyTree>,
override val template: Obligation.Terms<P>
) : NetState<P>

View File

@ -5,7 +5,10 @@ import com.pholser.junit.quickcheck.generator.Generator
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
import com.pholser.junit.quickcheck.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>(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>(CommandData::class.java) {
class CommandGenerator : Generator<Command>(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))
}
}

View File

@ -7,16 +7,15 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
import net.corda.core.contracts.Amount
import net.corda.core.contracts.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<Currency>,
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()

View File

@ -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<OwnableState>,
val price: Amount<Currency>,
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<SignedTransaction>(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<SignaturesFromSeller>(otherParty, stx).unwrap { it }
}
private fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
private fun signWithOurKeys(cashSigningPubKeys: List<PublicKeyTree>, ptx: TransactionBuilder): SignedTransaction {
// Now sign the transaction with whatever keys we need to move the cash.
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<TransactionBuilder, List<PublicKey>> {
private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKeyTree>> {
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)

View File

@ -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)

View File

@ -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<Currency>, dest: PublicKey): WireTransaction {
fun makeSpend(amount: Amount<Currency>, dest: PublicKeyTree): WireTransaction {
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
databaseTransaction(database) {
vault.generateSpend(tx, amount, dest)

View File

@ -2,13 +2,13 @@ package net.corda.contracts.asset
import net.corda.contracts.asset.Obligation.Lifecycle
import net.corda.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<Pair<PublicKey, PublicKey>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
val expected: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
val actual = netAmountsDue(balanced)
assertEquals(expected, actual)
}
@ -851,8 +851,8 @@ class ObligationTests {
@Test
fun `summing empty balances due between parties`() {
val empty = emptyMap<Pair<PublicKey, PublicKey>, Amount<Currency>>()
val expected = emptyMap<PublicKey, Long>()
val empty = emptyMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<Currency>>()
val expected = emptyMap<PublicKeyTree, Long>()
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<PublicKey, Long> = emptyMap() // Zero balances are stripped before returning
val expected: Map<PublicKeyTree, Long> = emptyMap() // Zero balances are stripped before returning
val actual = sumAmountsDue(balanced)
assertEquals(expected, actual)
}

View File

@ -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))
}

View File

@ -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<PublicKey, Party>()
private val keyToParties = ConcurrentHashMap<PublicKeyTree, Party>()
private val nameToParties = ConcurrentHashMap<String, Party>()
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]
}

View File

@ -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 }

View File

@ -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 {

View File

@ -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<Observable<Any>>? = 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<Observable<Any>>? = 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 ->

View File

@ -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()
}

View File

@ -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<NodeRegistration>, sig: DigitalSignature.WithKey) : SignedData<NodeRegistration>(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))
}
}

View File

@ -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<Currency>,
to: PublicKey,
onlyFromParties: Set<Party>?): Pair<TransactionBuilder, List<PublicKey>> {
to: PublicKeyTree,
onlyFromParties: Set<Party>?): Pair<TransactionBuilder, List<PublicKeyTree>> {
// 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<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKeyTree)
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
/**
@ -292,7 +293,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>): 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)

View File

@ -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<String>, val owningKey: Column<PublicKey>)
data class PartyColumns(val name: Column<String>, val owningKey: Column<PublicKeyTree>)
data class StateRefColumns(val txId: Column<SecureHash>, val index: Column<Int>)
/**
* [Table] column helpers for use with Exposed, as per [varchar] etc.
*/
fun Table.publicKey(name: String) = this.registerColumn<PublicKey>(name, PublicKeyColumnType)
fun Table.publicKeyTree(name: String) = this.registerColumn<PublicKeyTree>(name, PublicKeyTreeColumnType)
fun Table.secureHash(name: String) = this.registerColumn<SecureHash>(name, SecureHashColumnType)
fun Table.party(nameColumnName: String, keyColumnName: String) = PartyColumns(this.varchar(nameColumnName, length = 255), this.publicKey(keyColumnName))
fun Table.party(nameColumnName: String, keyColumnName: String) = PartyColumns(this.varchar(nameColumnName, length = 255), this.publicKeyTree(keyColumnName))
fun Table.uuidString(name: String) = this.registerColumn<UUID>(name, UUIDStringColumnType)
fun Table.localDate(name: String) = this.registerColumn<LocalDate>(name, LocalDateColumnType)
fun Table.localDateTime(name: String) = this.registerColumn<LocalDateTime>(name, LocalDateTimeColumnType)
@ -163,6 +166,15 @@ object PublicKeyColumnType : ColumnType() {
override fun notNullValueToDB(value: Any): Any = if (value is PublicKey) value.toBase58String() else value
}
/**
* [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].
*/

View File

@ -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))
}
)
}

View File

@ -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<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
withError: Boolean,
owner: PublicKey = BOB_PUBKEY,
owner: PublicKeyTree = BOB_PUBKEY,
notary: Party): Pair<Vault, List<WireTransaction>> {
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<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
withError: Boolean,
owner: PublicKey,
owner: PublicKeyTree,
amount: Amount<Issued<Currency>>,
attachmentID: SecureHash?,
notary: Party): Pair<Vault, List<WireTransaction>> {

View File

@ -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
}

View File

@ -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<IllegalStateException> {
nodeA.netMapCache.getNodeByPublicKey(keyPair.public)
nodeA.netMapCache.getNodeByPublicKey(keyPair.public.tree)
}
}
}

View File

@ -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<PublicKey>
override val participants: List<PublicKeyTree>
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

View File

@ -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()

View File

@ -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)

View File

@ -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<LinearState>(0))
signWith(DUMMY_NOTARY_KEY)
}.toSignedTransaction()

View File

@ -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)

View File

@ -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<PublicKey> = listOf(),
override val participants: List<PublicKeyTree> = listOf(),
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
return participants.any { ourKeys.contains(it) }
return participants.any { it.containsAny(ourKeys) }
}
}
}

View File

@ -9,7 +9,6 @@ import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.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<PublicKey>, commandData: CommandData) {
override fun _command(signers: List<PublicKeyTree>, commandData: CommandData) {
val command = Command(commandData, signers)
transactionBuilder.addCommand(command)
}
@ -325,7 +324,7 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>)
(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)
}

View File

@ -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<PublicKey>, commandData: CommandData)
fun _command(signers: List<PublicKeyTree>, commandData: CommandData)
/**
* Adds a timestamp to the transaction.
@ -99,12 +99,12 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(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.

View File

@ -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<Party>) : IdentityService, SingletonSerializeAsToken() {
private val keyToParties: Map<PublicKey, Party>
private val keyToParties: Map<PublicKeyTree, Party>
get() = synchronized(identities) { identities.associateBy { it.owningKey } }
private val nameToParties: Map<String, Party>
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]
}

View File

@ -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<StateNode> by fxid()
private val outputs: ListView<StateNode> by fxid()
private val signatures: ListView<PublicKey> by fxid()
private val signatures: ListView<PublicKeyTree> by fxid()
private val inputPane: TitledPane by fxid()
private val 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<NetworkIdentityModel>(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<NetworkIdentityModel>(TransactionViewer::class).lookup(key)
text = "${key.toStringShort()} (${nodeInfo?.legalIdentity?.name ?: "???"})"
text = "$key (${nodeInfo?.legalIdentity?.name ?: "???"})"
}
prefHeight = 185.0
}