mirror of
https://github.com/corda/corda.git
synced 2024-12-20 05:28:21 +00:00
Replace PublicKey with PublicKeyTree in Party. A single entity can now be identified by more than one key.
This commit is contained in:
parent
d7defd3157
commit
c33c55eb20
@ -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
|
||||
}
|
||||
)
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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) }.
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
@ -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()
|
@ -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)
|
||||
|
||||
}
|
||||
/**
|
||||
|
@ -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?
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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?
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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".
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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 ->
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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() }
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
Binary file not shown.
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)) {
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()));
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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 }
|
||||
|
@ -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 {
|
||||
|
@ -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 ->
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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].
|
||||
*/
|
||||
|
@ -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))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -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>> {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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]
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user