mirror of
https://github.com/corda/corda.git
synced 2024-12-24 15:16:45 +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)
|
require(tx.tx.outputs.size == 1)
|
||||||
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
||||||
// Only Alice signed
|
// Only Alice signed
|
||||||
require(signaturePubKeys.size == 1)
|
val aliceKey = aliceNode.legalIdentity.owningKey
|
||||||
require(signaturePubKeys.contains(aliceNode.legalIdentity.owningKey))
|
require(signaturePubKeys.size <= aliceKey.keys.size)
|
||||||
|
require(aliceKey.isFulfilledBy(signaturePubKeys))
|
||||||
issueTx = tx
|
issueTx = tx
|
||||||
},
|
},
|
||||||
// MOVE
|
// MOVE
|
||||||
@ -184,9 +185,8 @@ class NodeMonitorModelTest {
|
|||||||
require(tx.tx.outputs.size == 1)
|
require(tx.tx.outputs.size == 1)
|
||||||
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
||||||
// Alice and Notary signed
|
// Alice and Notary signed
|
||||||
require(signaturePubKeys.size == 2)
|
require(aliceNode.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys))
|
||||||
require(signaturePubKeys.contains(aliceNode.legalIdentity.owningKey))
|
require(notaryNode.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys))
|
||||||
require(signaturePubKeys.contains(notaryNode.notaryIdentity.owningKey))
|
|
||||||
moveTx = tx
|
moveTx = tx
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package net.corda.client.model
|
package net.corda.client.model
|
||||||
|
|
||||||
|
import javafx.collections.ObservableList
|
||||||
|
import kotlinx.support.jdk8.collections.removeIf
|
||||||
import net.corda.client.fxutils.foldToObservableList
|
import net.corda.client.fxutils.foldToObservableList
|
||||||
import net.corda.client.fxutils.map
|
import net.corda.client.fxutils.map
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.NetworkMapCache
|
import net.corda.core.node.services.NetworkMapCache
|
||||||
import net.corda.node.services.network.NetworkMapService
|
import net.corda.node.services.network.NetworkMapService
|
||||||
import javafx.collections.ObservableList
|
|
||||||
import kotlinx.support.jdk8.collections.removeIf
|
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
class NetworkIdentityModel {
|
class NetworkIdentityModel {
|
||||||
private val networkIdentityObservable by observable(NodeMonitorModel::networkMap)
|
private val networkIdentityObservable by observable(NodeMonitorModel::networkMap)
|
||||||
@ -34,7 +34,7 @@ class NetworkIdentityModel {
|
|||||||
return advertisedServices.any { it.info.type == NetworkMapService.type || it.info.type.isNotary() }
|
return advertisedServices.any { it.info.type == NetworkMapService.type || it.info.type.isNotary() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun lookup(publicKey: PublicKey): NodeInfo? {
|
fun lookup(publicKeyTree: PublicKeyTree): NodeInfo? {
|
||||||
return parties.firstOrNull { it.legalIdentity.owningKey == publicKey } ?: notaries.firstOrNull { it.notaryIdentity.owningKey == publicKey }
|
return parties.firstOrNull { it.legalIdentity.owningKey == publicKeyTree } ?: notaries.firstOrNull { it.notaryIdentity.owningKey == publicKeyTree }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import java.security.PublicKey
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,7 +60,7 @@ inline fun <R> requireThat(body: Requirements.() -> R) = R.body()
|
|||||||
//// Authenticated commands ///////////////////////////////////////////////////////////////////////////////////////////
|
//// Authenticated commands ///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/** Filters the command list by type, party and public key all at once. */
|
/** Filters the command list by type, party and public key all at once. */
|
||||||
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signer: PublicKey? = null,
|
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signer: PublicKeyTree? = null,
|
||||||
party: Party? = null) =
|
party: Party? = null) =
|
||||||
filter { it.value is T }.
|
filter { it.value is T }.
|
||||||
filter { if (signer == null) true else signer in it.signers }.
|
filter { if (signer == null) true else signer in it.signers }.
|
||||||
@ -68,7 +68,7 @@ inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>
|
|||||||
map { AuthenticatedObject(it.signers, it.signingParties, it.value as T) }
|
map { AuthenticatedObject(it.signers, it.signingParties, it.value as T) }
|
||||||
|
|
||||||
/** Filters the command list by type, parties and public keys all at once. */
|
/** Filters the command list by type, parties and public keys all at once. */
|
||||||
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signers: Collection<PublicKey>?,
|
inline fun <reified T : CommandData> Collection<AuthenticatedObject<CommandData>>.select(signers: Collection<PublicKeyTree>?,
|
||||||
parties: Collection<Party>?) =
|
parties: Collection<Party>?) =
|
||||||
filter { it.value is T }.
|
filter { it.value is T }.
|
||||||
filter { if (signers == null) true else it.signers.containsAll(signers)}.
|
filter { if (signers == null) true else it.signers.containsAll(signers)}.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
||||||
|
|
||||||
@ -15,12 +15,12 @@ class DummyContract : Contract {
|
|||||||
val magicNumber: Int
|
val magicNumber: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKey) : OwnableState, State {
|
data class SingleOwnerState(override val magicNumber: Int = 0, override val owner: PublicKeyTree) : OwnableState, State {
|
||||||
override val contract = DUMMY_PROGRAM_ID
|
override val contract = DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,9 +29,9 @@ class DummyContract : Contract {
|
|||||||
* in a different field, however this is a good example of a contract with multiple states.
|
* in a different field, however this is a good example of a contract with multiple states.
|
||||||
*/
|
*/
|
||||||
data class MultiOwnerState(override val magicNumber: Int = 0,
|
data class MultiOwnerState(override val magicNumber: Int = 0,
|
||||||
val owners: List<PublicKey>) : ContractState, State {
|
val owners: List<PublicKeyTree>) : ContractState, State {
|
||||||
override val contract = DUMMY_PROGRAM_ID
|
override val contract = DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = owners
|
get() = owners
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +54,8 @@ class DummyContract : Contract {
|
|||||||
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owner.party.owningKey))
|
return TransactionType.General.Builder(notary = notary).withItems(state, Command(Commands.Create(), owner.party.owningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKey) = move(listOf(prior), newOwner)
|
fun move(prior: StateAndRef<DummyContract.SingleOwnerState>, newOwner: PublicKeyTree) = move(listOf(prior), newOwner)
|
||||||
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKey): TransactionBuilder {
|
fun move(priors: List<StateAndRef<DummyContract.SingleOwnerState>>, newOwner: PublicKeyTree): TransactionBuilder {
|
||||||
require(priors.size > 0)
|
require(priors.size > 0)
|
||||||
val priorState = priors[0].state.data
|
val priorState = priors[0].state.data
|
||||||
val (cmd, state) = priorState.withNewOwner(newOwner)
|
val (cmd, state) = priorState.withNewOwner(newOwner)
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import java.security.PublicKey
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dummy state for use in testing. Not part of any contract, not even the [DummyContract].
|
* Dummy state for use in testing. Not part of any contract, not even the [DummyContract].
|
||||||
*/
|
*/
|
||||||
data class DummyState(val magicNumber: Int = 0) : ContractState {
|
data class DummyState(val magicNumber: Int = 0) : ContractState {
|
||||||
override val contract = DUMMY_PROGRAM_ID
|
override val contract = DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = emptyList()
|
get() = emptyList()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import java.security.PublicKey
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
|
|
||||||
class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() {
|
class InsufficientBalanceException(val amountMissing: Amount<*>) : Exception() {
|
||||||
override fun toString() = "Insufficient balance, missing $amountMissing"
|
override fun toString() = "Insufficient balance, missing $amountMissing"
|
||||||
@ -26,10 +26,11 @@ interface FungibleAsset<T> : OwnableState {
|
|||||||
* There must be an ExitCommand signed by these keys to destroy the amount. While all states require their
|
* There must be an ExitCommand signed by these keys to destroy the amount. While all states require their
|
||||||
* owner to sign, some (i.e. cash) also require the issuer.
|
* owner to sign, some (i.e. cash) also require the issuer.
|
||||||
*/
|
*/
|
||||||
val exitKeys: Collection<PublicKey>
|
val exitKeys: Collection<PublicKeyTree>
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||||
override val owner: PublicKey
|
override val owner: PublicKeyTree
|
||||||
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKey): FungibleAsset<T>
|
|
||||||
|
fun move(newAmount: Amount<Issued<T>>, newOwner: PublicKeyTree): FungibleAsset<T>
|
||||||
|
|
||||||
// Just for grouping
|
// Just for grouping
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
|
@ -2,8 +2,8 @@ package net.corda.core.contracts
|
|||||||
|
|
||||||
import net.corda.core.contracts.clauses.Clause
|
import net.corda.core.contracts.clauses.Clause
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.node.services.ServiceType
|
import net.corda.core.node.services.ServiceType
|
||||||
import net.corda.core.protocols.ProtocolLogicRef
|
import net.corda.core.protocols.ProtocolLogicRef
|
||||||
import net.corda.core.protocols.ProtocolLogicRefFactory
|
import net.corda.core.protocols.ProtocolLogicRefFactory
|
||||||
@ -113,7 +113,7 @@ interface ContractState {
|
|||||||
* The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants
|
* The participants list should normally be derived from the contents of the state. E.g. for [Cash] the participants
|
||||||
* list should just contain the owner.
|
* list should just contain the owner.
|
||||||
*/
|
*/
|
||||||
val participants: List<PublicKey>
|
val participants: List<PublicKeyTree>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All contract states may be _encumbered_ by up to one other state.
|
* All contract states may be _encumbered_ by up to one other state.
|
||||||
@ -184,10 +184,10 @@ fun <T> Amount<Issued<T>>.withoutIssuer(): Amount<T> = Amount(quantity, token.pr
|
|||||||
*/
|
*/
|
||||||
interface OwnableState : ContractState {
|
interface OwnableState : ContractState {
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||||
val owner: PublicKey
|
val owner: PublicKeyTree
|
||||||
|
|
||||||
/** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */
|
/** Copies the underlying data structure, replacing the owner field with this new value and leaving the rest alone */
|
||||||
fun withNewOwner(newOwner: PublicKey): Pair<CommandData, OwnableState>
|
fun withNewOwner(newOwner: PublicKeyTree): Pair<CommandData, OwnableState>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Something which is scheduled to happen at a point in time */
|
/** Something which is scheduled to happen at a point in time */
|
||||||
@ -351,15 +351,15 @@ abstract class TypeOnlyCommandData : CommandData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Command data/content plus pubkey pair: the signature is stored at the end of the serialized bytes */
|
/** Command data/content plus pubkey pair: the signature is stored at the end of the serialized bytes */
|
||||||
data class Command(val value: CommandData, val signers: List<PublicKey>) {
|
data class Command(val value: CommandData, val signers: List<PublicKeyTree>) {
|
||||||
init {
|
init {
|
||||||
require(signers.isNotEmpty())
|
require(signers.isNotEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(data: CommandData, key: PublicKey) : this(data, listOf(key))
|
constructor(data: CommandData, key: PublicKeyTree) : this(data, listOf(key))
|
||||||
|
|
||||||
private fun commandDataToString() = value.toString().let { if (it.contains("@")) it.replace('$', '.').split("@")[0] else it }
|
private fun commandDataToString() = value.toString().let { if (it.contains("@")) it.replace('$', '.').split("@")[0] else it }
|
||||||
override fun toString() = "${commandDataToString()} with pubkeys ${signers.map { it.toStringShort() }}"
|
override fun toString() = "${commandDataToString()} with pubkeys ${signers.joinToString()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A common issue command, to enforce that issue commands have a nonce value. */
|
/** A common issue command, to enforce that issue commands have a nonce value. */
|
||||||
@ -386,7 +386,7 @@ interface NetCommand : CommandData {
|
|||||||
|
|
||||||
/** Wraps an object that was signed by a public key, which may be a well known/recognised institutional key. */
|
/** Wraps an object that was signed by a public key, which may be a well known/recognised institutional key. */
|
||||||
data class AuthenticatedObject<out T : Any>(
|
data class AuthenticatedObject<out T : Any>(
|
||||||
val signers: List<PublicKey>,
|
val signers: List<PublicKeyTree>,
|
||||||
/** If any public keys were recognised, the looked up institutions are available here */
|
/** If any public keys were recognised, the looked up institutions are available here */
|
||||||
val signingParties: List<Party>,
|
val signingParties: List<Party>,
|
||||||
val value: T
|
val value: T
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/** Defines transaction build & validation logic for a specific transaction type */
|
/** Defines transaction build & validation logic for a specific transaction type */
|
||||||
sealed class TransactionType {
|
sealed class TransactionType {
|
||||||
@ -25,7 +25,7 @@ sealed class TransactionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Check that the list of signers includes all the necessary keys */
|
/** Check that the list of signers includes all the necessary keys */
|
||||||
fun verifySigners(tx: LedgerTransaction): Set<PublicKey> {
|
fun verifySigners(tx: LedgerTransaction): Set<PublicKeyTree> {
|
||||||
val notaryKey = tx.inputs.map { it.state.notary.owningKey }.toSet()
|
val notaryKey = tx.inputs.map { it.state.notary.owningKey }.toSet()
|
||||||
if (notaryKey.size > 1) throw TransactionVerificationException.MoreThanOneNotary(tx)
|
if (notaryKey.size > 1) throw TransactionVerificationException.MoreThanOneNotary(tx)
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ sealed class TransactionType {
|
|||||||
* Return the list of public keys that that require signatures for the transaction type.
|
* Return the list of public keys that that require signatures for the transaction type.
|
||||||
* Note: the notary key is checked separately for all transactions and need not be included.
|
* Note: the notary key is checked separately for all transactions and need not be included.
|
||||||
*/
|
*/
|
||||||
abstract fun getRequiredSigners(tx: LedgerTransaction): Set<PublicKey>
|
abstract fun getRequiredSigners(tx: LedgerTransaction): Set<PublicKeyTree>
|
||||||
|
|
||||||
/** Implement type specific transaction validation logic */
|
/** Implement type specific transaction validation logic */
|
||||||
abstract fun verifyTransaction(tx: LedgerTransaction)
|
abstract fun verifyTransaction(tx: LedgerTransaction)
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
// TODO: Consider moving this out of the core module and providing a different way for unit tests to test contracts.
|
// TODO: Consider moving this out of the core module and providing a different way for unit tests to test contracts.
|
||||||
@ -94,8 +93,8 @@ class TransactionConflictException(val conflictRef: StateRef, val tx1: LedgerTra
|
|||||||
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : Exception(cause) {
|
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : Exception(cause) {
|
||||||
class ContractRejection(tx: LedgerTransaction, val contract: Contract, cause: Throwable?) : TransactionVerificationException(tx, cause)
|
class ContractRejection(tx: LedgerTransaction, val contract: Contract, cause: Throwable?) : TransactionVerificationException(tx, cause)
|
||||||
class MoreThanOneNotary(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
|
class MoreThanOneNotary(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
|
||||||
class SignersMissing(tx: LedgerTransaction, val missing: List<PublicKey>) : TransactionVerificationException(tx, null) {
|
class SignersMissing(tx: LedgerTransaction, val missing: List<PublicKeyTree>) : TransactionVerificationException(tx, null) {
|
||||||
override fun toString() = "Signers missing: ${missing.map { it.toStringShort() }}"
|
override fun toString() = "Signers missing: ${missing.joinToString()}"
|
||||||
}
|
}
|
||||||
class InvalidNotaryChange(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
|
class InvalidNotaryChange(tx: LedgerTransaction) : TransactionVerificationException(tx, null)
|
||||||
class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) {
|
class NotaryChangeInWrongTransactionType(tx: LedgerTransaction, val outputNotary: Party) : TransactionVerificationException(tx, null) {
|
||||||
|
@ -31,7 +31,8 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
|
|||||||
fun verifyWithECDSA(content: OpaqueBytes) = by.verifyWithECDSA(content.bits, this)
|
fun verifyWithECDSA(content: OpaqueBytes) = by.verifyWithECDSA(content.bits, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey, bits)
|
// TODO: consider removing this as whoever needs to identify the signer should be able to derive it from the public key
|
||||||
|
class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey.singleKey, bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
object NullPublicKey : PublicKey, Comparable<PublicKey> {
|
object NullPublicKey : PublicKey, Comparable<PublicKey> {
|
||||||
@ -42,6 +43,8 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
|
|||||||
override fun toString() = "NULL_KEY"
|
override fun toString() = "NULL_KEY"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val NullPublicKeyTree = NullPublicKey.tree
|
||||||
|
|
||||||
// TODO: Clean up this duplication between Null and Dummy public key
|
// TODO: Clean up this duplication between Null and Dummy public key
|
||||||
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
|
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
|
||||||
override fun getAlgorithm() = "DUMMY"
|
override fun getAlgorithm() = "DUMMY"
|
||||||
@ -78,7 +81,7 @@ fun KeyPair.signWithECDSA(bitsToSign: ByteArray) = private.signWithECDSA(bitsToS
|
|||||||
fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes) = private.signWithECDSA(bitsToSign.bits, public)
|
fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes) = private.signWithECDSA(bitsToSign.bits, public)
|
||||||
fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes, party: Party) = signWithECDSA(bitsToSign.bits, party)
|
fun KeyPair.signWithECDSA(bitsToSign: OpaqueBytes, party: Party) = signWithECDSA(bitsToSign.bits, party)
|
||||||
fun KeyPair.signWithECDSA(bitsToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
|
fun KeyPair.signWithECDSA(bitsToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
|
||||||
check(public == party.owningKey)
|
check(public in party.owningKey.keys)
|
||||||
val sig = signWithECDSA(bitsToSign)
|
val sig = signWithECDSA(bitsToSign)
|
||||||
return DigitalSignature.LegallyIdentifiable(party, sig.bits)
|
return DigitalSignature.LegallyIdentifiable(party, sig.bits)
|
||||||
}
|
}
|
||||||
@ -99,8 +102,6 @@ fun PublicKey.toStringShort(): String {
|
|||||||
} ?: toString()
|
} ?: toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Iterable<PublicKey>.toStringsShort(): String = map { it.toStringShort() }.toString()
|
|
||||||
|
|
||||||
/** Creates a [PublicKeyTree] with a single leaf node containing the public key */
|
/** Creates a [PublicKeyTree] with a single leaf node containing the public key */
|
||||||
val PublicKey.tree: PublicKeyTree get() = PublicKeyTree.Leaf(this)
|
val PublicKey.tree: PublicKeyTree get() = PublicKeyTree.Leaf(this)
|
||||||
|
|
||||||
|
@ -4,8 +4,26 @@ import net.corda.core.contracts.PartyAndReference
|
|||||||
import net.corda.core.serialization.OpaqueBytes
|
import net.corda.core.serialization.OpaqueBytes
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
/** A [Party] is well known (name, pubkey) pair. In a real system this would probably be an X.509 certificate. */
|
/**
|
||||||
data class Party(val name: String, val owningKey: PublicKey) {
|
* The [Party] class represents an entity on the network, which is typically identified by a legal [name] and public key
|
||||||
|
* that it can sign transactions under. As parties may use multiple keys for signing and, for example, have offline backup
|
||||||
|
* keys, the "public key" of a party is represented by a composite construct – a [PublicKeyTree], which combines multiple
|
||||||
|
* cryptographic public key primitives into a tree structure.
|
||||||
|
*
|
||||||
|
* For example: Alice has two key pairs (pub1/priv1 and pub2/priv2), and wants to be able to sign transactions with either of them.
|
||||||
|
* Her advertised [Party] then has a legal [name] "Alice" and an [owingKey] "pub1 or pub2".
|
||||||
|
*
|
||||||
|
* [Party] is also used for service identities. E.g. Alice may also be running an interest rate oracle on her Corda node,
|
||||||
|
* which requires a separate signing key (and an identifying name). Services can also be distributed – run by a coordinated
|
||||||
|
* cluster of Corda nodes. A [Party] representing a distributed service will have a public key tree composed of the
|
||||||
|
* individual cluster nodes' public keys. Each of the nodes in the cluster will advertise the same group [Party].
|
||||||
|
*
|
||||||
|
* @see PublicKeyTree
|
||||||
|
*/
|
||||||
|
data class Party(val name: String, val owningKey: PublicKeyTree) {
|
||||||
|
/** A helper constructor that converts the given [PublicKey] in to a [PublicKeyTree] with a single node */
|
||||||
|
constructor(name: String, owningKey: PublicKey) : this(name, owningKey.tree)
|
||||||
|
|
||||||
override fun toString() = name
|
override fun toString() = name
|
||||||
|
|
||||||
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes)
|
fun ref(bytes: OpaqueBytes) = PartyAndReference(this, bytes)
|
||||||
|
@ -26,10 +26,10 @@ sealed class PublicKeyTree {
|
|||||||
fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key))
|
fun isFulfilledBy(key: PublicKey) = isFulfilledBy(setOf(key))
|
||||||
|
|
||||||
/** Returns all [PublicKey]s contained within the tree leaves */
|
/** Returns all [PublicKey]s contained within the tree leaves */
|
||||||
abstract fun getKeys(): Set<PublicKey>
|
abstract val keys: Set<PublicKey>
|
||||||
|
|
||||||
/** Checks whether any of the given [keys] matches a leaf on the tree */
|
/** Checks whether any of the given [keys] matches a leaf on the tree */
|
||||||
fun containsAny(keys: Iterable<PublicKey>) = getKeys().intersect(keys).isNotEmpty()
|
fun containsAny(otherKeys: Iterable<PublicKey>) = keys.intersect(otherKeys).isNotEmpty()
|
||||||
|
|
||||||
// TODO: implement a proper encoding/decoding mechanism
|
// TODO: implement a proper encoding/decoding mechanism
|
||||||
fun toBase58String(): String = Base58.encode(this.serialize().bits)
|
fun toBase58String(): String = Base58.encode(this.serialize().bits)
|
||||||
@ -42,21 +42,17 @@ sealed class PublicKeyTree {
|
|||||||
class Leaf(val publicKey: PublicKey) : PublicKeyTree() {
|
class Leaf(val publicKey: PublicKey) : PublicKeyTree() {
|
||||||
override fun isFulfilledBy(keys: Iterable<PublicKey>) = publicKey in keys
|
override fun isFulfilledBy(keys: Iterable<PublicKey>) = publicKey in keys
|
||||||
|
|
||||||
override fun getKeys(): Set<PublicKey> = setOf(publicKey)
|
override val keys: Set<PublicKey>
|
||||||
|
get() = setOf(publicKey)
|
||||||
|
|
||||||
// Auto-generated. TODO: remove once data class inheritance is enabled
|
// TODO: remove once data class inheritance is enabled
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
return this === other || other is Leaf && other.publicKey == this.publicKey
|
||||||
if (other?.javaClass != javaClass) return false
|
|
||||||
|
|
||||||
other as Leaf
|
|
||||||
|
|
||||||
if (publicKey != other.publicKey) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode() = publicKey.hashCode()
|
override fun hashCode() = publicKey.hashCode()
|
||||||
|
|
||||||
|
override fun toString() = publicKey.toStringShort()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,7 +74,8 @@ sealed class PublicKeyTree {
|
|||||||
return totalWeight >= threshold
|
return totalWeight >= threshold
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getKeys(): Set<PublicKey> = children.flatMap { it.getKeys() }.toSet()
|
override val keys: Set<PublicKey>
|
||||||
|
get() = children.flatMap { it.keys }.toSet()
|
||||||
|
|
||||||
// Auto-generated. TODO: remove once data class inheritance is enabled
|
// Auto-generated. TODO: remove once data class inheritance is enabled
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@ -100,6 +97,8 @@ sealed class PublicKeyTree {
|
|||||||
result = 31 * result + children.hashCode()
|
result = 31 * result + children.hashCode()
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString() = "(${children.joinToString()})"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A helper class for building a [PublicKeyTree.Node]. */
|
/** A helper class for building a [PublicKeyTree.Node]. */
|
||||||
@ -119,8 +118,7 @@ sealed class PublicKeyTree {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addLeaves(publicKeys: List<PublicKey>): Builder = addLeaves(*publicKeys.toTypedArray())
|
fun addKeys(publicKeys: List<PublicKeyTree>): Builder = addKeys(*publicKeys.toTypedArray())
|
||||||
fun addLeaves(vararg publicKeys: PublicKey) = addKeys(*publicKeys.map { it.tree }.toTypedArray())
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the [PublicKeyTree.Node]. If [threshold] is not specified, it will default to
|
* Builds the [PublicKeyTree.Node]. If [threshold] is not specified, it will default to
|
||||||
@ -131,7 +129,16 @@ sealed class PublicKeyTree {
|
|||||||
else Node(threshold ?: children.size, children.toList(), weights.toList())
|
else Node(threshold ?: children.size, children.toList(), weights.toList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the enclosed [PublicKey] for a [PublicKeyTree] with a single node
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the [PublicKeyTree] contains more than one node
|
||||||
|
*/
|
||||||
|
val singleKey: PublicKey
|
||||||
|
get() = keys.singleOrNull() ?: throw IllegalStateException("The public key tree has more than one node")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the set of all [PublicKey]s contained in the leaves of the [PublicKeyTree]s */
|
/** Returns the set of all [PublicKey]s contained in the leaves of the [PublicKeyTree]s */
|
||||||
fun Iterable<PublicKeyTree>.getKeys() = flatMap { it.getKeys() }.toSet()
|
val Iterable<PublicKeyTree>.keys: Set<PublicKey>
|
||||||
|
get() = flatMap { it.keys }.toSet()
|
@ -1,13 +1,13 @@
|
|||||||
package net.corda.core.node
|
package net.corda.core.node
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionResolutionException
|
import net.corda.core.contracts.TransactionResolutionException
|
||||||
import net.corda.core.contracts.TransactionState
|
import net.corda.core.contracts.TransactionState
|
||||||
import net.corda.core.messaging.MessagingService
|
import net.corda.core.messaging.MessagingService
|
||||||
import net.corda.core.node.services.*
|
import net.corda.core.node.services.*
|
||||||
import net.corda.core.protocols.ProtocolLogic
|
import net.corda.core.protocols.ProtocolLogic
|
||||||
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
|
|
||||||
@ -59,8 +59,11 @@ interface ServiceHub {
|
|||||||
* Helper property to shorten code for fetching the Node's KeyPair associated with the
|
* Helper property to shorten code for fetching the Node's KeyPair associated with the
|
||||||
* public legalIdentity Party from the key management service.
|
* public legalIdentity Party from the key management service.
|
||||||
* Typical use is during signing in protocols and for unit test signing.
|
* Typical use is during signing in protocols and for unit test signing.
|
||||||
|
*
|
||||||
|
* TODO: legalIdentity can now be composed of multiple keys, should we return a list of keyPairs here? Right now
|
||||||
|
* the logic assumes the legal identity has a public key tree with only one node
|
||||||
*/
|
*/
|
||||||
val legalIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.legalIdentity.owningKey)
|
val legalIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.legalIdentity.owningKey.keys)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper property to shorten code for fetching the Node's KeyPair associated with the
|
* Helper property to shorten code for fetching the Node's KeyPair associated with the
|
||||||
@ -69,7 +72,7 @@ interface ServiceHub {
|
|||||||
* an IllegalArgumentException.
|
* an IllegalArgumentException.
|
||||||
* Typical use is during signing in protocols and for unit test signing.
|
* Typical use is during signing in protocols and for unit test signing.
|
||||||
*/
|
*/
|
||||||
val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey)
|
val notaryIdentityKey: KeyPair get() = this.keyManagementService.toKeyPair(this.myInfo.notaryIdentity.owningKey.keys)
|
||||||
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.core.node.services
|
package net.corda.core.node.services
|
||||||
|
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import java.security.PublicKey
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An identity service maintains an bidirectional map of [Party]s to their associated public keys and thus supports
|
* An identity service maintains an bidirectional map of [Party]s to their associated public keys and thus supports
|
||||||
@ -15,6 +15,6 @@ interface IdentityService {
|
|||||||
// indefinitely. It may be that in the long term we need to drop or archive very old Party information for space,
|
// indefinitely. It may be that in the long term we need to drop or archive very old Party information for space,
|
||||||
// but for now this is not supported.
|
// but for now this is not supported.
|
||||||
|
|
||||||
fun partyFromKey(key: PublicKey): Party?
|
fun partyFromKey(key: PublicKeyTree): Party?
|
||||||
fun partyFromName(name: String): Party?
|
fun partyFromName(name: String): Party?
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ import com.google.common.annotations.VisibleForTesting
|
|||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import net.corda.core.contracts.Contract
|
import net.corda.core.contracts.Contract
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.messaging.MessagingService
|
import net.corda.core.messaging.MessagingService
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A network map contains lists of nodes on the network along with information about their identity keys, services
|
* A network map contains lists of nodes on the network along with information about their identity keys, services
|
||||||
@ -74,7 +74,7 @@ interface NetworkMapCache {
|
|||||||
/**
|
/**
|
||||||
* Look up the node info for a public key.
|
* Look up the node info for a public key.
|
||||||
*/
|
*/
|
||||||
fun getNodeByPublicKey(publicKey: PublicKey): NodeInfo?
|
fun getNodeByPublicKey(publicKey: PublicKeyTree): NodeInfo?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a network map service; fetches a copy of the latest map from the service and subscribes to any further
|
* Add a network map service; fetches a copy of the latest map from the service and subscribes to any further
|
||||||
|
@ -4,7 +4,9 @@ import com.google.common.util.concurrent.ListenableFuture
|
|||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -184,8 +186,8 @@ interface VaultService {
|
|||||||
@Throws(InsufficientBalanceException::class)
|
@Throws(InsufficientBalanceException::class)
|
||||||
fun generateSpend(tx: TransactionBuilder,
|
fun generateSpend(tx: TransactionBuilder,
|
||||||
amount: Amount<Currency>,
|
amount: Amount<Currency>,
|
||||||
to: PublicKey,
|
to: PublicKeyTree,
|
||||||
onlyFromParties: Set<Party>? = null): Pair<TransactionBuilder, List<PublicKey>>
|
onlyFromParties: Set<Party>? = null): Pair<TransactionBuilder, List<PublicKeyTree>>
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java)
|
inline fun <reified T : LinearState> VaultService.linearHeadsOfType() = linearHeadsOfType_(T::class.java)
|
||||||
@ -201,14 +203,19 @@ inline fun <reified T : DealState> VaultService.dealsWith(party: Party) = linear
|
|||||||
* The current interface is obviously not usable for those use cases: this is just where we'd put a real signing
|
* The current interface is obviously not usable for those use cases: this is just where we'd put a real signing
|
||||||
* interface if/when one is developed.
|
* interface if/when one is developed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface KeyManagementService {
|
interface KeyManagementService {
|
||||||
/** Returns a snapshot of the current pubkey->privkey mapping. */
|
/** Returns a snapshot of the current pubkey->privkey mapping. */
|
||||||
val keys: Map<PublicKey, PrivateKey>
|
val keys: Map<PublicKey, PrivateKey>
|
||||||
|
|
||||||
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key")
|
// TODO: make toPrivate return null if not found instead of throwing
|
||||||
|
fun toPrivate(publicKey: PublicKey) = keys[publicKey] ?: throw IllegalStateException("No private key known for requested public key ${publicKey.toStringShort()}")
|
||||||
|
|
||||||
fun toKeyPair(publicKey: PublicKey) = KeyPair(publicKey, toPrivate(publicKey))
|
fun toKeyPair(publicKey: PublicKey) = KeyPair(publicKey, toPrivate(publicKey))
|
||||||
|
|
||||||
|
/** Returns the first [KeyPair] matching any of the [publicKeys] */
|
||||||
|
fun toKeyPair(publicKeys: Iterable<PublicKey>) = publicKeys.first { keys.contains(it) }.let { toKeyPair(it) }
|
||||||
|
|
||||||
/** Generates a new random key and adds it to the exposed map. */
|
/** Generates a new random key and adds it to the exposed map. */
|
||||||
fun freshKey(): KeyPair
|
fun freshKey(): KeyPair
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,8 @@ import com.esotericsoftware.kryo.io.Input
|
|||||||
import com.esotericsoftware.kryo.io.Output
|
import com.esotericsoftware.kryo.io.Output
|
||||||
import com.esotericsoftware.kryo.serializers.JavaSerializer
|
import com.esotericsoftware.kryo.serializers.JavaSerializer
|
||||||
import com.esotericsoftware.kryo.serializers.MapSerializer
|
import com.esotericsoftware.kryo.serializers.MapSerializer
|
||||||
|
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
||||||
|
import de.javakaffee.kryoserializers.guava.*
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.node.AttachmentsClassLoader
|
import net.corda.core.node.AttachmentsClassLoader
|
||||||
@ -18,9 +20,6 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.NonEmptySet
|
import net.corda.core.utilities.NonEmptySet
|
||||||
import net.corda.core.utilities.NonEmptySetSerializer
|
import net.corda.core.utilities.NonEmptySetSerializer
|
||||||
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
|
||||||
import de.javakaffee.kryoserializers.guava.*
|
|
||||||
import net.corda.core.crypto.*
|
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||||
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
|
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
|
||||||
@ -32,7 +31,6 @@ import java.io.ObjectOutputStream
|
|||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
@ -268,7 +266,7 @@ object WireTransactionSerializer : Serializer<WireTransaction>() {
|
|||||||
val outputs = kryo.readClassAndObject(input) as List<TransactionState<ContractState>>
|
val outputs = kryo.readClassAndObject(input) as List<TransactionState<ContractState>>
|
||||||
val commands = kryo.readClassAndObject(input) as List<Command>
|
val commands = kryo.readClassAndObject(input) as List<Command>
|
||||||
val notary = kryo.readClassAndObject(input) as Party?
|
val notary = kryo.readClassAndObject(input) as Party?
|
||||||
val signers = kryo.readClassAndObject(input) as List<PublicKey>
|
val signers = kryo.readClassAndObject(input) as List<PublicKeyTree>
|
||||||
val transactionType = kryo.readClassAndObject(input) as TransactionType
|
val transactionType = kryo.readClassAndObject(input) as TransactionType
|
||||||
val timestamp = kryo.readClassAndObject(input) as Timestamp?
|
val timestamp = kryo.readClassAndObject(input) as Timestamp?
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ import com.pholser.junit.quickcheck.generator.java.lang.StringGenerator
|
|||||||
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
|
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
|
||||||
import com.pholser.junit.quickcheck.random.SourceOfRandomness
|
import com.pholser.junit.quickcheck.random.SourceOfRandomness
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.entropyToKeyPair
|
|
||||||
import net.corda.core.serialization.OpaqueBytes
|
import net.corda.core.serialization.OpaqueBytes
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -41,9 +39,15 @@ class PublicKeyGenerator: Generator<PublicKey>(PublicKey::class.java) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PublicKeyTreeGenerator : Generator<PublicKeyTree>(PublicKeyTree::class.java) {
|
||||||
|
override fun generate(random: SourceOfRandomness, status: GenerationStatus): PublicKeyTree {
|
||||||
|
return entropyToKeyPair(random.nextBigInteger(32)).public.tree
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PartyGenerator: Generator<Party>(Party::class.java) {
|
class PartyGenerator: Generator<Party>(Party::class.java) {
|
||||||
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Party {
|
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Party {
|
||||||
return Party(StringGenerator().generate(random, status), PublicKeyGenerator().generate(random, status))
|
return Party(StringGenerator().generate(random, status), PublicKeyTreeGenerator().generate(random, status))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package net.corda.core.transactions
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import java.security.PublicKey
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +25,7 @@ abstract class BaseTransaction(
|
|||||||
* transaction until the transaction is verified by using [LedgerTransaction.verify]. It includes the
|
* transaction until the transaction is verified by using [LedgerTransaction.verify]. It includes the
|
||||||
* notary key, if the notary field is set.
|
* notary key, if the notary field is set.
|
||||||
*/
|
*/
|
||||||
val mustSign: List<PublicKey>,
|
val mustSign: List<PublicKeyTree>,
|
||||||
/**
|
/**
|
||||||
* Pointer to a class that defines the behaviour of this transaction: either normal, or "notary changing".
|
* Pointer to a class that defines the behaviour of this transaction: either normal, or "notary changing".
|
||||||
*/
|
*/
|
||||||
|
@ -2,8 +2,8 @@ package net.corda.core.transactions
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations:
|
* A LedgerTransaction is derived from a [WireTransaction]. It is the result of doing the following operations:
|
||||||
@ -27,7 +27,7 @@ class LedgerTransaction(
|
|||||||
/** The hash of the original serialised WireTransaction. */
|
/** The hash of the original serialised WireTransaction. */
|
||||||
override val id: SecureHash,
|
override val id: SecureHash,
|
||||||
notary: Party?,
|
notary: Party?,
|
||||||
signers: List<PublicKey>,
|
signers: List<PublicKeyTree>,
|
||||||
timestamp: Timestamp?,
|
timestamp: Timestamp?,
|
||||||
type: TransactionType
|
type: TransactionType
|
||||||
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {
|
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {
|
||||||
|
@ -3,12 +3,11 @@ package net.corda.core.transactions
|
|||||||
import net.corda.core.contracts.NamedByHash
|
import net.corda.core.contracts.NamedByHash
|
||||||
import net.corda.core.contracts.TransactionResolutionException
|
import net.corda.core.contracts.TransactionResolutionException
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toStringsShort
|
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.security.PublicKey
|
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -34,9 +33,9 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
|
|||||||
/** Lazily calculated access to the deserialised/hashed transaction data. */
|
/** Lazily calculated access to the deserialised/hashed transaction data. */
|
||||||
val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) }
|
val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) }
|
||||||
|
|
||||||
class SignaturesMissingException(val missing: Set<PublicKey>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() {
|
class SignaturesMissingException(val missing: Set<PublicKeyTree>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() {
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.toStringsShort()}"
|
return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
|
|||||||
* @throws SignaturesMissingException if any signatures should have been present but were not.
|
* @throws SignaturesMissingException if any signatures should have been present but were not.
|
||||||
*/
|
*/
|
||||||
@Throws(SignatureException::class)
|
@Throws(SignatureException::class)
|
||||||
fun verifySignatures(vararg allowedToBeMissing: PublicKey): WireTransaction {
|
fun verifySignatures(vararg allowedToBeMissing: PublicKeyTree): WireTransaction {
|
||||||
// Embedded WireTransaction is not deserialised until after we check the signatures.
|
// Embedded WireTransaction is not deserialised until after we check the signatures.
|
||||||
checkSignaturesAreValid()
|
checkSignaturesAreValid()
|
||||||
|
|
||||||
@ -83,19 +82,17 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMissingSignatures(): Set<PublicKey> {
|
private fun getMissingSignatures(): Set<PublicKeyTree> {
|
||||||
val requiredKeys = tx.mustSign.toSet()
|
|
||||||
val sigKeys = sigs.map { it.by }.toSet()
|
val sigKeys = sigs.map { it.by }.toSet()
|
||||||
|
val missing = tx.mustSign.filter { !it.isFulfilledBy(sigKeys) }.toSet()
|
||||||
if (sigKeys.containsAll(requiredKeys)) return emptySet()
|
return missing
|
||||||
return requiredKeys - sigKeys
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a human readable description of where signatures are required from, and are missing, to assist in debugging
|
* Get a human readable description of where signatures are required from, and are missing, to assist in debugging
|
||||||
* the underlying cause.
|
* the underlying cause.
|
||||||
*/
|
*/
|
||||||
private fun getMissingKeyDescriptions(missing: Set<PublicKey>): ArrayList<String> {
|
private fun getMissingKeyDescriptions(missing: Set<PublicKeyTree>): ArrayList<String> {
|
||||||
// TODO: We need a much better way of structuring this data
|
// TODO: We need a much better way of structuring this data
|
||||||
val missingElements = ArrayList<String>()
|
val missingElements = ArrayList<String>()
|
||||||
this.tx.commands.forEach { command ->
|
this.tx.commands.forEach { command ->
|
||||||
|
@ -3,11 +3,7 @@ package net.corda.core.transactions
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.transactions.WireTransaction
|
|
||||||
import net.corda.core.crypto.*
|
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -35,7 +31,7 @@ open class TransactionBuilder(
|
|||||||
protected val attachments: MutableList<SecureHash> = arrayListOf(),
|
protected val attachments: MutableList<SecureHash> = arrayListOf(),
|
||||||
protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(),
|
protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(),
|
||||||
protected val commands: MutableList<Command> = arrayListOf(),
|
protected val commands: MutableList<Command> = arrayListOf(),
|
||||||
protected val signers: MutableSet<PublicKey> = mutableSetOf(),
|
protected val signers: MutableSet<PublicKeyTree> = mutableSetOf(),
|
||||||
protected var timestamp: Timestamp? = null) {
|
protected var timestamp: Timestamp? = null) {
|
||||||
|
|
||||||
val time: Timestamp? get() = timestamp
|
val time: Timestamp? get() = timestamp
|
||||||
@ -124,7 +120,7 @@ open class TransactionBuilder(
|
|||||||
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
* @throws IllegalArgumentException if the signature key doesn't appear in any command.
|
||||||
*/
|
*/
|
||||||
fun checkSignature(sig: DigitalSignature.WithKey) {
|
fun checkSignature(sig: DigitalSignature.WithKey) {
|
||||||
require(commands.any { it.signers.contains(sig.by) }) { "Signature key doesn't match any command" }
|
require(commands.any { it.signers.any { sig.by in it.keys } }) { "Signature key doesn't match any command" }
|
||||||
sig.verifyWithECDSA(toWireTransaction().id)
|
sig.verifyWithECDSA(toWireTransaction().id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,9 +136,9 @@ open class TransactionBuilder(
|
|||||||
fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction {
|
fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction {
|
||||||
if (checkSufficientSignatures) {
|
if (checkSufficientSignatures) {
|
||||||
val gotKeys = currentSigs.map { it.by }.toSet()
|
val gotKeys = currentSigs.map { it.by }.toSet()
|
||||||
val missing: Set<PublicKey> = signers - gotKeys
|
val missing: Set<PublicKeyTree> = signers.filter { !it.isFulfilledBy(gotKeys) }.toSet()
|
||||||
if (missing.isNotEmpty())
|
if (missing.isNotEmpty())
|
||||||
throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.toStringsShort()}")
|
throw IllegalStateException("Missing signatures on the transaction for the public keys: ${missing.joinToString()}")
|
||||||
}
|
}
|
||||||
val wtx = toWireTransaction()
|
val wtx = toWireTransaction()
|
||||||
return SignedTransaction(wtx.serialize(), ArrayList(currentSigs), wtx.id)
|
return SignedTransaction(wtx.serialize(), ArrayList(currentSigs), wtx.id)
|
||||||
@ -182,8 +178,8 @@ open class TransactionBuilder(
|
|||||||
commands.add(arg)
|
commands.add(arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys)))
|
fun addCommand(data: CommandData, vararg keys: PublicKeyTree) = addCommand(Command(data, listOf(*keys)))
|
||||||
fun addCommand(data: CommandData, keys: List<PublicKey>) = addCommand(Command(data, keys))
|
fun addCommand(data: CommandData, keys: List<PublicKeyTree>) = addCommand(Command(data, keys))
|
||||||
|
|
||||||
// Accessors that yield immutable snapshots.
|
// Accessors that yield immutable snapshots.
|
||||||
fun inputStates(): List<StateRef> = ArrayList(inputs)
|
fun inputStates(): List<StateRef> = ArrayList(inputs)
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.core.transactions
|
|||||||
import com.esotericsoftware.kryo.Kryo
|
import com.esotericsoftware.kryo.Kryo
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.indexOfOrThrow
|
import net.corda.core.indexOfOrThrow
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
@ -29,7 +30,7 @@ class WireTransaction(
|
|||||||
/** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */
|
/** Ordered list of ([CommandData], [PublicKey]) pairs that instruct the contracts what to do. */
|
||||||
val commands: List<Command>,
|
val commands: List<Command>,
|
||||||
notary: Party?,
|
notary: Party?,
|
||||||
signers: List<PublicKey>,
|
signers: List<PublicKeyTree>,
|
||||||
type: TransactionType,
|
type: TransactionType,
|
||||||
timestamp: Timestamp?
|
timestamp: Timestamp?
|
||||||
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {
|
) : BaseTransaction(inputs, outputs, notary, signers, type, timestamp) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package net.corda.core.utilities
|
package net.corda.core.utilities
|
||||||
|
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.parsePublicKeyBase58
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import javax.ws.rs.core.Response
|
import javax.ws.rs.core.Response
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ class ApiUtils(val services: ServiceHub) {
|
|||||||
*/
|
*/
|
||||||
fun withParty(partyKeyStr: String, notFound: (String) -> Response = defaultNotFound, found: (Party) -> Response): Response {
|
fun withParty(partyKeyStr: String, notFound: (String) -> Response = defaultNotFound, found: (Party) -> Response): Response {
|
||||||
return try {
|
return try {
|
||||||
val partyKey = parsePublicKeyBase58(partyKeyStr)
|
val partyKey = PublicKeyTree.parseFromBase58(partyKeyStr)
|
||||||
val party = services.identityService.partyFromKey(partyKey)
|
val party = services.identityService.partyFromKey(partyKey)
|
||||||
if(party == null) notFound("Unknown party") else found(party)
|
if(party == null) notFound("Unknown party") else found(party)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
|
@ -2,20 +2,15 @@
|
|||||||
package net.corda.core.utilities
|
package net.corda.core.utilities
|
||||||
|
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.DummyPublicKey
|
|
||||||
import net.corda.core.crypto.Party
|
|
||||||
import net.corda.core.crypto.entropyToKeyPair
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
// A dummy time at which we will be pretending test transactions are created.
|
// A dummy time at which we will be pretending test transactions are created.
|
||||||
val TEST_TX_TIME: Instant get() = Instant.parse("2015-04-17T12:00:00.00Z")
|
val TEST_TX_TIME: Instant get() = Instant.parse("2015-04-17T12:00:00.00Z")
|
||||||
|
|
||||||
val DUMMY_PUBKEY_1: PublicKey get() = DummyPublicKey("x1")
|
val DUMMY_PUBKEY_1: PublicKeyTree get() = DummyPublicKey("x1").tree
|
||||||
val DUMMY_PUBKEY_2: PublicKey get() = DummyPublicKey("x2")
|
val DUMMY_PUBKEY_2: PublicKeyTree get() = DummyPublicKey("x2").tree
|
||||||
|
|
||||||
val DUMMY_KEY_1: KeyPair by lazy { generateKeyPair() }
|
val DUMMY_KEY_1: KeyPair by lazy { generateKeyPair() }
|
||||||
val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() }
|
val DUMMY_KEY_2: KeyPair by lazy { generateKeyPair() }
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.contracts.StateAndRef
|
|||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.signWithECDSA
|
import net.corda.core.crypto.signWithECDSA
|
||||||
import net.corda.core.node.recordTransactions
|
import net.corda.core.node.recordTransactions
|
||||||
import net.corda.core.protocols.ProtocolLogic
|
import net.corda.core.protocols.ProtocolLogic
|
||||||
@ -15,7 +16,6 @@ import net.corda.core.utilities.ProgressTracker
|
|||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
import net.corda.protocols.AbstractStateReplacementProtocol.Acceptor
|
import net.corda.protocols.AbstractStateReplacementProtocol.Acceptor
|
||||||
import net.corda.protocols.AbstractStateReplacementProtocol.Instigator
|
import net.corda.protocols.AbstractStateReplacementProtocol.Instigator
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract protocol to be used for replacing one state with another, for example when changing the notary of a state.
|
* Abstract protocol to be used for replacing one state with another, for example when changing the notary of a state.
|
||||||
@ -67,10 +67,10 @@ abstract class AbstractStateReplacementProtocol<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract protected fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal<T>
|
abstract protected fun assembleProposal(stateRef: StateRef, modification: T, stx: SignedTransaction): Proposal<T>
|
||||||
abstract protected fun assembleTx(): Pair<SignedTransaction, List<PublicKey>>
|
abstract protected fun assembleTx(): Pair<SignedTransaction, List<PublicKeyTree>>
|
||||||
|
|
||||||
@Suspendable
|
@Suspendable
|
||||||
private fun collectSignatures(participants: List<PublicKey>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
private fun collectSignatures(participants: List<PublicKeyTree>, stx: SignedTransaction): List<DigitalSignature.WithKey> {
|
||||||
val parties = participants.map {
|
val parties = participants.map {
|
||||||
val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(it) ?:
|
val participantNode = serviceHub.networkMapCache.getNodeByPublicKey(it) ?:
|
||||||
throw IllegalStateException("Participant $it to state $originalState not found on the network")
|
throw IllegalStateException("Participant $it to state $originalState not found on the network")
|
||||||
@ -95,7 +95,7 @@ abstract class AbstractStateReplacementProtocol<T> {
|
|||||||
val participantSignature = response.unwrap {
|
val participantSignature = response.unwrap {
|
||||||
if (it.sig == null) throw StateReplacementException(it.error!!)
|
if (it.sig == null) throw StateReplacementException(it.error!!)
|
||||||
else {
|
else {
|
||||||
check(it.sig.by == party.owningKey) { "Not signed by the required participant" }
|
check(party.owningKey.isFulfilledBy(it.sig.by)) { "Not signed by the required participant" }
|
||||||
it.sig.verifyWithECDSA(stx.id)
|
it.sig.verifyWithECDSA(stx.id)
|
||||||
it.sig
|
it.sig
|
||||||
}
|
}
|
||||||
|
@ -50,5 +50,9 @@ class FinalityProtocol(val transaction: SignedTransaction,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun needsNotarySignature(stx: SignedTransaction) = stx.tx.notary != null && hasNoNotarySignature(stx)
|
private fun needsNotarySignature(stx: SignedTransaction) = stx.tx.notary != null && hasNoNotarySignature(stx)
|
||||||
private fun hasNoNotarySignature(stx: SignedTransaction) = stx.tx.notary?.owningKey !in stx.sigs.map { it.by }
|
private fun hasNoNotarySignature(stx: SignedTransaction): Boolean {
|
||||||
|
val notaryKey = stx.tx.notary?.owningKey
|
||||||
|
val signers = stx.sigs.map { it.by }.toSet()
|
||||||
|
return !(notaryKey?.isFulfilledBy(signers) ?: false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,12 @@ import net.corda.core.contracts.StateAndRef
|
|||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.contracts.TransactionType
|
import net.corda.core.contracts.TransactionType
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
import net.corda.protocols.NotaryChangeProtocol.Acceptor
|
import net.corda.protocols.NotaryChangeProtocol.Acceptor
|
||||||
import net.corda.protocols.NotaryChangeProtocol.Instigator
|
import net.corda.protocols.NotaryChangeProtocol.Instigator
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A protocol to be used for changing a state's Notary. This is required since all input states to a transaction
|
* A protocol to be used for changing a state's Notary. This is required since all input states to a transaction
|
||||||
@ -36,7 +36,7 @@ object NotaryChangeProtocol: AbstractStateReplacementProtocol<Party>() {
|
|||||||
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal<Party>
|
override fun assembleProposal(stateRef: StateRef, modification: Party, stx: SignedTransaction): AbstractStateReplacementProtocol.Proposal<Party>
|
||||||
= Proposal(stateRef, modification, stx)
|
= Proposal(stateRef, modification, stx)
|
||||||
|
|
||||||
override fun assembleTx(): Pair<SignedTransaction, List<PublicKey>> {
|
override fun assembleTx(): Pair<SignedTransaction, List<PublicKeyTree>> {
|
||||||
val state = originalState.state
|
val state = originalState.state
|
||||||
val newState = state.withNotary(modification)
|
val newState = state.withNotary(modification)
|
||||||
val participants = state.data.participants
|
val participants = state.data.participants
|
||||||
|
@ -1,21 +1,16 @@
|
|||||||
package net.corda.protocols
|
package net.corda.protocols
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.Party
|
|
||||||
import net.corda.core.crypto.SignedData
|
|
||||||
import net.corda.core.crypto.signWithECDSA
|
|
||||||
import net.corda.core.node.services.TimestampChecker
|
import net.corda.core.node.services.TimestampChecker
|
||||||
import net.corda.core.node.services.UniquenessException
|
import net.corda.core.node.services.UniquenessException
|
||||||
import net.corda.core.node.services.UniquenessProvider
|
import net.corda.core.node.services.UniquenessProvider
|
||||||
import net.corda.core.protocols.ProtocolLogic
|
import net.corda.core.protocols.ProtocolLogic
|
||||||
import net.corda.core.serialization.SerializedBytes
|
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
object NotaryProtocol {
|
object NotaryProtocol {
|
||||||
|
|
||||||
@ -177,5 +172,5 @@ sealed class NotaryError {
|
|||||||
|
|
||||||
class TransactionInvalid : NotaryError()
|
class TransactionInvalid : NotaryError()
|
||||||
|
|
||||||
class SignaturesMissing(val missingSigners: Set<PublicKey>) : NotaryError()
|
class SignaturesMissing(val missingSigners: Set<PublicKeyTree>) : NotaryError()
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package net.corda.protocols
|
package net.corda.protocols
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.core.TransientProperty
|
import net.corda.core.contracts.ContractState
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.DealState
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.signWithECDSA
|
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.recordTransactions
|
import net.corda.core.node.recordTransactions
|
||||||
import net.corda.core.node.services.ServiceType
|
import net.corda.core.node.services.ServiceType
|
||||||
@ -17,9 +16,7 @@ import net.corda.core.transactions.WireTransaction
|
|||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.UntrustworthyData
|
import net.corda.core.utilities.UntrustworthyData
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes for manipulating a two party deal or agreement.
|
* Classes for manipulating a two party deal or agreement.
|
||||||
@ -42,7 +39,7 @@ object TwoPartyDealProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
|
// This object is serialised to the network and is the first protocol message the seller sends to the buyer.
|
||||||
data class Handshake<out T>(val payload: T, val publicKey: PublicKey)
|
data class Handshake<out T>(val payload: T, val publicKey: PublicKeyTree)
|
||||||
|
|
||||||
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.LegallyIdentifiable)
|
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySig: DigitalSignature.LegallyIdentifiable)
|
||||||
|
|
||||||
@ -90,7 +87,7 @@ object TwoPartyDealProtocol {
|
|||||||
progressTracker.currentStep = AWAITING_PROPOSAL
|
progressTracker.currentStep = AWAITING_PROPOSAL
|
||||||
|
|
||||||
// Make the first message we'll send to kick off the protocol.
|
// Make the first message we'll send to kick off the protocol.
|
||||||
val hello = Handshake(payload, myKeyPair.public)
|
val hello = Handshake(payload, myKeyPair.public.tree)
|
||||||
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
|
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
|
||||||
|
|
||||||
return maybeSTX
|
return maybeSTX
|
||||||
@ -104,7 +101,7 @@ object TwoPartyDealProtocol {
|
|||||||
progressTracker.nextStep()
|
progressTracker.nextStep()
|
||||||
|
|
||||||
// Check that the tx proposed by the buyer is valid.
|
// Check that the tx proposed by the buyer is valid.
|
||||||
val wtx: WireTransaction = stx.verifySignatures(myKeyPair.public, notaryNode.notaryIdentity.owningKey)
|
val wtx: WireTransaction = stx.verifySignatures(myKeyPair.public.tree, notaryNode.notaryIdentity.owningKey)
|
||||||
logger.trace { "Received partially signed transaction: ${stx.id}" }
|
logger.trace { "Received partially signed transaction: ${stx.id}" }
|
||||||
|
|
||||||
checkDependencies(stx)
|
checkDependencies(stx)
|
||||||
@ -251,18 +248,18 @@ object TwoPartyDealProtocol {
|
|||||||
return sendAndReceive<SignaturesFromPrimary>(otherParty, stx).unwrap { it }
|
return sendAndReceive<SignaturesFromPrimary>(otherParty, stx).unwrap { it }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun signWithOurKeys(signingPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
|
private fun signWithOurKeys(signingPubKeys: List<PublicKeyTree>, ptx: TransactionBuilder): SignedTransaction {
|
||||||
// Now sign the transaction with whatever keys we need to move the cash.
|
// Now sign the transaction with whatever keys we need to move the cash.
|
||||||
for (k in signingPubKeys) {
|
for (publicKey in signingPubKeys.keys) {
|
||||||
val priv = serviceHub.keyManagementService.toPrivate(k)
|
val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
|
||||||
ptx.signWith(KeyPair(k, priv))
|
ptx.signWith(KeyPair(publicKey, privateKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptx.toSignedTransaction(checkSufficientSignatures = false)
|
return ptx.toSignedTransaction(checkSufficientSignatures = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suspendable protected abstract fun validateHandshake(handshake: Handshake<U>): Handshake<U>
|
@Suspendable protected abstract fun validateHandshake(handshake: Handshake<U>): Handshake<U>
|
||||||
@Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<PublicKey>>
|
@Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<PublicKeyTree>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -295,7 +292,7 @@ object TwoPartyDealProtocol {
|
|||||||
return handshake.copy(payload = autoOffer.copy(dealBeingOffered = deal))
|
return handshake.copy(payload = autoOffer.copy(dealBeingOffered = deal))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun assembleSharedTX(handshake: Handshake<AutoOffer>): Pair<TransactionBuilder, List<PublicKey>> {
|
override fun assembleSharedTX(handshake: Handshake<AutoOffer>): Pair<TransactionBuilder, List<PublicKeyTree>> {
|
||||||
val deal = handshake.payload.dealBeingOffered
|
val deal = handshake.payload.dealBeingOffered
|
||||||
val ptx = deal.generateAgreement(handshake.payload.notary)
|
val ptx = deal.generateAgreement(handshake.payload.notary)
|
||||||
|
|
||||||
|
Binary file not shown.
@ -1,12 +1,14 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_1
|
import net.corda.core.utilities.DUMMY_PUBKEY_1
|
||||||
import net.corda.core.utilities.DUMMY_PUBKEY_2
|
import net.corda.core.utilities.DUMMY_PUBKEY_2
|
||||||
import net.corda.testing.*
|
import net.corda.testing.MEGA_CORP
|
||||||
|
import net.corda.testing.ledger
|
||||||
|
import net.corda.testing.transaction
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
@ -42,7 +44,7 @@ class TransactionEncumbranceTests {
|
|||||||
data class State(
|
data class State(
|
||||||
val validFrom: Instant
|
val validFrom: Instant
|
||||||
) : ContractState {
|
) : ContractState {
|
||||||
override val participants: List<PublicKey> = emptyList()
|
override val participants: List<PublicKeyTree> = emptyList()
|
||||||
override val contract: Contract = TEST_TIMELOCK_ID
|
override val contract: Contract = TEST_TIMELOCK_ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package net.corda.core.contracts
|
package net.corda.core.contracts
|
||||||
|
|
||||||
import net.corda.core.crypto.newSecureRandom
|
import net.corda.core.crypto.newSecureRandom
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
||||||
import net.corda.testing.node.MockTransactionStorage
|
|
||||||
import net.corda.testing.MEGA_CORP_KEY
|
import net.corda.testing.MEGA_CORP_KEY
|
||||||
|
import net.corda.testing.node.MockTransactionStorage
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -31,7 +32,7 @@ class TransactionGraphSearchTests {
|
|||||||
fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage {
|
fun buildTransactions(command: CommandData, signer: KeyPair): GraphTransactionStorage {
|
||||||
val originTx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val originTx = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyState(random31BitValue()))
|
addOutputState(DummyState(random31BitValue()))
|
||||||
addCommand(command, signer.public)
|
addCommand(command, signer.public.tree)
|
||||||
signWith(signer)
|
signWith(signer)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction(false)
|
}.toSignedTransaction(false)
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
|||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.signWithECDSA
|
import net.corda.core.crypto.signWithECDSA
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.transactions.LedgerTransaction
|
import net.corda.core.transactions.LedgerTransaction
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -29,7 +30,7 @@ class TransactionTests {
|
|||||||
outputs = emptyList(),
|
outputs = emptyList(),
|
||||||
commands = emptyList(),
|
commands = emptyList(),
|
||||||
notary = DUMMY_NOTARY,
|
notary = DUMMY_NOTARY,
|
||||||
signers = listOf(DUMMY_KEY_1.public, DUMMY_KEY_2.public),
|
signers = listOf(DUMMY_KEY_1.public.tree, DUMMY_KEY_2.public.tree),
|
||||||
type = TransactionType.General(),
|
type = TransactionType.General(),
|
||||||
timestamp = null
|
timestamp = null
|
||||||
)
|
)
|
||||||
@ -38,20 +39,20 @@ class TransactionTests {
|
|||||||
assertFailsWith<IllegalArgumentException> { make().verifySignatures() }
|
assertFailsWith<IllegalArgumentException> { make().verifySignatures() }
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(DUMMY_KEY_1.public),
|
setOf(DUMMY_KEY_1.public.tree),
|
||||||
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_2).verifySignatures() }.missing
|
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_2).verifySignatures() }.missing
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(DUMMY_KEY_2.public),
|
setOf(DUMMY_KEY_2.public.tree),
|
||||||
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_1).verifySignatures() }.missing
|
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_KEY_1).verifySignatures() }.missing
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
setOf(DUMMY_KEY_2.public),
|
setOf(DUMMY_KEY_2.public.tree),
|
||||||
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public) }.missing
|
assertFailsWith<SignedTransaction.SignaturesMissingException> { make(DUMMY_CASH_ISSUER_KEY).verifySignatures(DUMMY_KEY_1.public.tree) }.missing
|
||||||
)
|
)
|
||||||
|
|
||||||
make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public)
|
make(DUMMY_KEY_1).verifySignatures(DUMMY_KEY_2.public.tree)
|
||||||
make(DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public)
|
make(DUMMY_KEY_2).verifySignatures(DUMMY_KEY_1.public.tree)
|
||||||
|
|
||||||
make(DUMMY_KEY_1, DUMMY_KEY_2).verifySignatures()
|
make(DUMMY_KEY_1, DUMMY_KEY_2).verifySignatures()
|
||||||
}
|
}
|
||||||
@ -64,7 +65,7 @@ class TransactionTests {
|
|||||||
val commands = emptyList<AuthenticatedObject<CommandData>>()
|
val commands = emptyList<AuthenticatedObject<CommandData>>()
|
||||||
val attachments = emptyList<Attachment>()
|
val attachments = emptyList<Attachment>()
|
||||||
val id = SecureHash.randomSHA256()
|
val id = SecureHash.randomSHA256()
|
||||||
val signers = listOf(DUMMY_NOTARY_KEY.public)
|
val signers = listOf(DUMMY_NOTARY_KEY.public.tree)
|
||||||
val timestamp: Timestamp? = null
|
val timestamp: Timestamp? = null
|
||||||
val transaction: LedgerTransaction = LedgerTransaction(
|
val transaction: LedgerTransaction = LedgerTransaction(
|
||||||
inputs,
|
inputs,
|
||||||
@ -91,7 +92,7 @@ class TransactionTests {
|
|||||||
val commands = emptyList<AuthenticatedObject<CommandData>>()
|
val commands = emptyList<AuthenticatedObject<CommandData>>()
|
||||||
val attachments = emptyList<Attachment>()
|
val attachments = emptyList<Attachment>()
|
||||||
val id = SecureHash.randomSHA256()
|
val id = SecureHash.randomSHA256()
|
||||||
val signers = listOf(DUMMY_NOTARY_KEY.public)
|
val signers = listOf(DUMMY_NOTARY_KEY.public.tree)
|
||||||
val timestamp: Timestamp? = null
|
val timestamp: Timestamp? = null
|
||||||
val transaction: LedgerTransaction = LedgerTransaction(
|
val transaction: LedgerTransaction = LedgerTransaction(
|
||||||
inputs,
|
inputs,
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.core.node
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.node.services.AttachmentStorage
|
import net.corda.core.node.services.AttachmentStorage
|
||||||
import net.corda.core.serialization.*
|
import net.corda.core.serialization.*
|
||||||
@ -15,7 +16,6 @@ import org.junit.Test
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.jar.JarOutputStream
|
import java.util.jar.JarOutputStream
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -37,7 +37,7 @@ class AttachmentClassLoaderTests {
|
|||||||
class AttachmentDummyContract : Contract {
|
class AttachmentDummyContract : Contract {
|
||||||
data class State(val magicNumber: Int = 0) : ContractState {
|
data class State(val magicNumber: Int = 0) : ContractState {
|
||||||
override val contract = ATTACHMENT_TEST_PROGRAM_ID
|
override val contract = ATTACHMENT_TEST_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf()
|
get() = listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package net.corda.core.node
|
package net.corda.core.node
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class VaultUpdateTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class DummyState : ContractState {
|
private class DummyState : ContractState {
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = emptyList()
|
get() = emptyList()
|
||||||
override val contract = VaultUpdateTests.DummyContract
|
override val contract = VaultUpdateTests.DummyContract
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package net.corda.core.serialization
|
package net.corda.core.serialization
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.seconds
|
import net.corda.core.seconds
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
@ -9,7 +11,6 @@ import net.corda.testing.MINI_CORP
|
|||||||
import net.corda.testing.generateStateRef
|
import net.corda.testing.generateStateRef
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -27,12 +28,12 @@ class TransactionSerializationTests {
|
|||||||
data class State(
|
data class State(
|
||||||
val deposit: PartyAndReference,
|
val deposit: PartyAndReference,
|
||||||
val amount: Amount<Currency>,
|
val amount: Amount<Currency>,
|
||||||
override val owner: PublicKey) : OwnableState {
|
override val owner: PublicKeyTree) : OwnableState {
|
||||||
override val contract: Contract = TEST_PROGRAM_ID
|
override val contract: Contract = TEST_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
interface Commands : CommandData {
|
interface Commands : CommandData {
|
||||||
class Move() : TypeOnlyCommandData(), Commands
|
class Move() : TypeOnlyCommandData(), Commands
|
||||||
@ -45,7 +46,7 @@ class TransactionSerializationTests {
|
|||||||
val fakeStateRef = generateStateRef()
|
val fakeStateRef = generateStateRef()
|
||||||
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY), fakeStateRef)
|
val inputState = StateAndRef(TransactionState(TestCash.State(depositRef, 100.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY), fakeStateRef)
|
||||||
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY)
|
val outputState = TransactionState(TestCash.State(depositRef, 600.POUNDS, DUMMY_PUBKEY_1), DUMMY_NOTARY)
|
||||||
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public), DUMMY_NOTARY)
|
val changeState = TransactionState(TestCash.State(depositRef, 400.POUNDS, DUMMY_KEY_1.public.tree), DUMMY_NOTARY)
|
||||||
|
|
||||||
|
|
||||||
lateinit var tx: TransactionBuilder
|
lateinit var tx: TransactionBuilder
|
||||||
@ -53,7 +54,7 @@ class TransactionSerializationTests {
|
|||||||
@Before
|
@Before
|
||||||
fun setup() {
|
fun setup() {
|
||||||
tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems(
|
tx = TransactionType.General.Builder(DUMMY_NOTARY).withItems(
|
||||||
inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(DUMMY_KEY_1.public))
|
inputState, outputState, changeState, Command(TestCash.Commands.Move(), arrayListOf(DUMMY_KEY_1.public.tree))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ class TransactionSerializationTests {
|
|||||||
// If the signature was replaced in transit, we don't like it.
|
// If the signature was replaced in transit, we don't like it.
|
||||||
assertFailsWith(SignatureException::class) {
|
assertFailsWith(SignatureException::class) {
|
||||||
val tx2 = TransactionType.General.Builder(DUMMY_NOTARY).withItems(inputState, outputState, changeState,
|
val tx2 = TransactionType.General.Builder(DUMMY_NOTARY).withItems(inputState, outputState, changeState,
|
||||||
Command(TestCash.Commands.Move(), DUMMY_KEY_2.public))
|
Command(TestCash.Commands.Move(), DUMMY_KEY_2.public.tree))
|
||||||
tx2.signWith(DUMMY_NOTARY_KEY)
|
tx2.signWith(DUMMY_NOTARY_KEY)
|
||||||
tx2.signWith(DUMMY_KEY_2)
|
tx2.signWith(DUMMY_KEY_2)
|
||||||
|
|
||||||
|
@ -2,10 +2,9 @@ package net.corda.contracts
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -44,24 +43,24 @@ class AccountReceivable : Contract {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
// technical variables
|
// technical variables
|
||||||
override val owner: PublicKey,
|
override val owner: PublicKeyTree,
|
||||||
val status: StatusEnum,
|
val status: StatusEnum,
|
||||||
val props: AccountReceivableProperties
|
val props: AccountReceivableProperties
|
||||||
|
|
||||||
) : OwnableState {
|
) : OwnableState {
|
||||||
override val contract = ACCOUNTRECEIVABLE_PROGRAM_ID
|
override val contract = ACCOUNTRECEIVABLE_PROGRAM_ID
|
||||||
|
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
override fun toString() = "AR owned by ${owner.toStringShort()})"
|
override fun toString() = "AR owned by $owner)"
|
||||||
|
|
||||||
fun checkInvoice(invoice: Invoice.State): Boolean {
|
fun checkInvoice(invoice: Invoice.State): Boolean {
|
||||||
val arProps = Helper.invoicePropsToARProps(invoice.props, props.discountRate)
|
val arProps = Helper.invoicePropsToARProps(invoice.props, props.discountRate)
|
||||||
return props == arProps
|
return props == arProps
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Issue(), copy(owner = newOwner, status = StatusEnum.Issued))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Issue(), copy(owner = newOwner, status = StatusEnum.Issued))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ package net.corda.contracts
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.days
|
import net.corda.core.days
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
@ -46,15 +46,15 @@ class BillOfLadingAgreement : Contract {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
// technical variables
|
// technical variables
|
||||||
override val owner: PublicKey,
|
override val owner: PublicKeyTree,
|
||||||
val beneficiary: Party,
|
val beneficiary: Party,
|
||||||
val props: BillOfLadingProperties
|
val props: BillOfLadingProperties
|
||||||
|
|
||||||
) : OwnableState {
|
) : OwnableState {
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey): Pair<CommandData, OwnableState> {
|
override fun withNewOwner(newOwner: PublicKeyTree): Pair<CommandData, OwnableState> {
|
||||||
return Pair(Commands.TransferPossession(), copy(owner = newOwner))
|
return Pair(Commands.TransferPossession(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ class BillOfLadingAgreement : Contract {
|
|||||||
/**
|
/**
|
||||||
* Returns a transaction that issues a Bill of Lading Agreement
|
* Returns a transaction that issues a Bill of Lading Agreement
|
||||||
*/
|
*/
|
||||||
fun generateIssue(owner: PublicKey, beneficiary: Party, props: BillOfLadingProperties, notary: Party): TransactionBuilder {
|
fun generateIssue(owner: PublicKeyTree, beneficiary: Party, props: BillOfLadingProperties, notary: Party): TransactionBuilder {
|
||||||
val state = State(owner, beneficiary, props)
|
val state = State(owner, beneficiary, props)
|
||||||
val builder = TransactionType.General.Builder(notary = notary)
|
val builder = TransactionType.General.Builder(notary = notary)
|
||||||
builder.setTime(Instant.now(), 1.days)
|
builder.setTime(Instant.now(), 1.days)
|
||||||
@ -122,17 +122,17 @@ class BillOfLadingAgreement : Contract {
|
|||||||
/**
|
/**
|
||||||
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
||||||
*/
|
*/
|
||||||
fun generateTransferAndEndorse(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKey, newBeneficiary: Party) {
|
fun generateTransferAndEndorse(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKeyTree, newBeneficiary: Party) {
|
||||||
tx.addInputState(BoL)
|
tx.addInputState(BoL)
|
||||||
tx.addOutputState(BoL.state.data.copy(owner = newOwner, beneficiary = newBeneficiary))
|
tx.addOutputState(BoL.state.data.copy(owner = newOwner, beneficiary = newBeneficiary))
|
||||||
val signers: List<PublicKey> = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey)
|
val signers: List<PublicKeyTree> = listOf(BoL.state.data.owner, BoL.state.data.beneficiary.owningKey)
|
||||||
tx.addCommand(Commands.TransferAndEndorseBL(), signers)
|
tx.addCommand(Commands.TransferAndEndorseBL(), signers)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
||||||
*/
|
*/
|
||||||
fun generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKey) {
|
fun generateTransferPossession(tx: TransactionBuilder, BoL: StateAndRef<State>, newOwner: PublicKeyTree) {
|
||||||
tx.addInputState(BoL)
|
tx.addInputState(BoL)
|
||||||
tx.addOutputState(BoL.state.data.copy(owner = newOwner))
|
tx.addOutputState(BoL.state.data.copy(owner = newOwner))
|
||||||
// tx.addOutputState(BoL.state.data.copy().withNewOwner(newOwner))
|
// tx.addOutputState(BoL.state.data.copy().withNewOwner(newOwner))
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.contracts
|
|||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -71,7 +72,7 @@ class Invoice : Contract {
|
|||||||
|
|
||||||
override val contract = INVOICE_PROGRAM_ID
|
override val contract = INVOICE_PROGRAM_ID
|
||||||
|
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf(owner.owningKey)
|
get() = listOf(owner.owningKey)
|
||||||
|
|
||||||
// returns true when the actual business properties of the
|
// returns true when the actual business properties of the
|
||||||
@ -86,7 +87,7 @@ class Invoice : Contract {
|
|||||||
val amount: Amount<Issued<Currency>> get() = props.amount
|
val amount: Amount<Issued<Currency>> get() = props.amount
|
||||||
|
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return owner.owningKey in ourKeys || buyer.owningKey in ourKeys
|
return owner.owningKey.containsAny(ourKeys) || buyer.owningKey.containsAny(ourKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package net.corda.contracts
|
package net.corda.contracts
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.NullPublicKey
|
import net.corda.core.crypto.NullPublicKeyTree
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.Period
|
import java.time.Period
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -108,14 +108,14 @@ class LCApplication : Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val owner: PublicKey,
|
val owner: PublicKeyTree,
|
||||||
val status: Status,
|
val status: Status,
|
||||||
val props: LCApplicationProperties
|
val props: LCApplicationProperties
|
||||||
) : ContractState {
|
) : ContractState {
|
||||||
|
|
||||||
override val contract = LC_APPLICATION_PROGRAM_ID
|
override val contract = LC_APPLICATION_PROGRAM_ID
|
||||||
|
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
// returns true when the actual business properties of the
|
// returns true when the actual business properties of the
|
||||||
@ -125,7 +125,7 @@ class LCApplication : Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// iterate over the goods list and sum up the price for each
|
// iterate over the goods list and sum up the price for each
|
||||||
fun withoutOwner() = copy(owner = NullPublicKey)
|
fun withoutOwner() = copy(owner = NullPublicKeyTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateApply(props: LCApplicationProperties, notary: Party, purchaseOrder: Attachment): TransactionBuilder {
|
fun generateApply(props: LCApplicationProperties, notary: Party, purchaseOrder: Attachment): TransactionBuilder {
|
||||||
|
@ -3,10 +3,10 @@ package net.corda.contracts
|
|||||||
import net.corda.contracts.asset.sumCashBy
|
import net.corda.contracts.asset.sumCashBy
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.days
|
import net.corda.core.days
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.Period
|
import java.time.Period
|
||||||
@ -58,7 +58,7 @@ class LOC : Contract {
|
|||||||
) : ContractState {
|
) : ContractState {
|
||||||
override val contract = LOC_PROGRAM_ID
|
override val contract = LOC_PROGRAM_ID
|
||||||
|
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf()
|
get() = listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ package net.corda.contracts.universal
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,7 +18,7 @@ val UNIVERSAL_PROGRAM_ID = UniversalContract()
|
|||||||
|
|
||||||
class UniversalContract : Contract {
|
class UniversalContract : Contract {
|
||||||
|
|
||||||
data class State(override val participants: List<PublicKey>,
|
data class State(override val participants: List<PublicKeyTree>,
|
||||||
val details: Arrangement) : ContractState {
|
val details: Arrangement) : ContractState {
|
||||||
override val contract = UNIVERSAL_PROGRAM_ID
|
override val contract = UNIVERSAL_PROGRAM_ID
|
||||||
}
|
}
|
||||||
@ -316,7 +316,7 @@ class UniversalContract : Contract {
|
|||||||
override val legalContractReference: SecureHash
|
override val legalContractReference: SecureHash
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKey) {
|
fun generateIssue(tx: TransactionBuilder, arrangement: Arrangement, at: PartyAndReference, notary: PublicKeyTree) {
|
||||||
check(tx.inputStates().isEmpty())
|
check(tx.inputStates().isEmpty())
|
||||||
tx.addOutputState(State(listOf(notary), arrangement))
|
tx.addOutputState(State(listOf(notary), arrangement))
|
||||||
tx.addCommand(Commands.Issue(), at.party.owningKey)
|
tx.addCommand(Commands.Issue(), at.party.owningKey)
|
||||||
|
@ -2,15 +2,11 @@ package net.corda.contracts.universal
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableSet
|
import com.google.common.collect.ImmutableSet
|
||||||
import com.google.common.collect.Sets
|
import com.google.common.collect.Sets
|
||||||
import net.corda.core.contracts.Amount
|
|
||||||
import net.corda.core.contracts.Frequency
|
import net.corda.core.contracts.Frequency
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import com.sun.org.apache.xpath.internal.operations.Bool
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sofusmortensen on 23/05/16.
|
* Created by sofusmortensen on 23/05/16.
|
||||||
@ -20,44 +16,44 @@ fun Instant.toLocalDate(): LocalDate = LocalDate.ofEpochDay(this.epochSecond / 6
|
|||||||
|
|
||||||
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
|
fun LocalDate.toInstant(): Instant = Instant.ofEpochSecond(this.toEpochDay() * 60 * 60 * 24)
|
||||||
|
|
||||||
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> =
|
private fun liablePartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKeyTree> =
|
||||||
when (arrangement) {
|
when (arrangement) {
|
||||||
is Zero -> ImmutableSet.of<PublicKey>()
|
is Zero -> ImmutableSet.of<PublicKeyTree>()
|
||||||
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
|
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
|
||||||
is And ->
|
is And ->
|
||||||
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
|
arrangement.arrangements.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
|
||||||
is Actions ->
|
is Actions ->
|
||||||
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
|
arrangement.actions.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(liablePartiesVisitor(k)) }).build()
|
||||||
is RollOut -> liablePartiesVisitor(arrangement.template)
|
is RollOut -> liablePartiesVisitor(arrangement.template)
|
||||||
is Continuation -> ImmutableSet.of<PublicKey>()
|
is Continuation -> ImmutableSet.of<PublicKeyTree>()
|
||||||
else -> throw IllegalArgumentException("liableParties " + arrangement)
|
else -> throw IllegalArgumentException("liableParties " + arrangement)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKey> =
|
private fun liablePartiesVisitor(action: Action): ImmutableSet<PublicKeyTree> =
|
||||||
if (action.actors.size != 1)
|
if (action.actors.size != 1)
|
||||||
liablePartiesVisitor(action.arrangement)
|
liablePartiesVisitor(action.arrangement)
|
||||||
else
|
else
|
||||||
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy()
|
Sets.difference(liablePartiesVisitor(action.arrangement), ImmutableSet.of(action.actors.single())).immutableCopy()
|
||||||
|
|
||||||
/** returns list of potentially liable parties for a given contract */
|
/** returns list of potentially liable parties for a given contract */
|
||||||
fun liableParties(contract: Arrangement): Set<PublicKey> = liablePartiesVisitor(contract)
|
fun liableParties(contract: Arrangement): Set<PublicKeyTree> = liablePartiesVisitor(contract)
|
||||||
|
|
||||||
private fun involvedPartiesVisitor(action: Action): Set<PublicKey> =
|
private fun involvedPartiesVisitor(action: Action): Set<PublicKeyTree> =
|
||||||
Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy()
|
Sets.union(involvedPartiesVisitor(action.arrangement), action.actors.map { it.owningKey }.toSet()).immutableCopy()
|
||||||
|
|
||||||
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKey> =
|
private fun involvedPartiesVisitor(arrangement: Arrangement): ImmutableSet<PublicKeyTree> =
|
||||||
when (arrangement) {
|
when (arrangement) {
|
||||||
is Zero -> ImmutableSet.of<PublicKey>()
|
is Zero -> ImmutableSet.of<PublicKeyTree>()
|
||||||
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
|
is Transfer -> ImmutableSet.of(arrangement.from.owningKey)
|
||||||
is And ->
|
is And ->
|
||||||
arrangement.arrangements.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
|
arrangement.arrangements.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
|
||||||
is Actions ->
|
is Actions ->
|
||||||
arrangement.actions.fold(ImmutableSet.builder<PublicKey>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
|
arrangement.actions.fold(ImmutableSet.builder<PublicKeyTree>(), { builder, k -> builder.addAll(involvedPartiesVisitor(k)) }).build()
|
||||||
else -> throw IllegalArgumentException()
|
else -> throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** returns list of involved parties for a given contract */
|
/** returns list of involved parties for a given contract */
|
||||||
fun involvedParties(arrangement: Arrangement): Set<PublicKey> = involvedPartiesVisitor(arrangement)
|
fun involvedParties(arrangement: Arrangement): Set<PublicKeyTree> = involvedPartiesVisitor(arrangement)
|
||||||
|
|
||||||
fun replaceParty(action: Action, from: Party, to: Party): Action =
|
fun replaceParty(action: Action, from: Party, to: Party): Action =
|
||||||
if (action.actors.contains(from)) {
|
if (action.actors.contains(from)) {
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
|
||||||
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
|
||||||
* set forth therein.
|
|
||||||
*
|
|
||||||
* All other rights reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.corda.contracts.isolated
|
package net.corda.contracts.isolated
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
// The dummy contract doesn't do anything useful. It exists for testing purposes.
|
||||||
|
|
||||||
@ -21,7 +13,7 @@ val ANOTHER_DUMMY_PROGRAM_ID = AnotherDummyContract()
|
|||||||
class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor {
|
class AnotherDummyContract : Contract, net.corda.core.node.DummyContractBackdoor {
|
||||||
data class State(val magicNumber: Int = 0) : ContractState {
|
data class State(val magicNumber: Int = 0) : ContractState {
|
||||||
override val contract = ANOTHER_DUMMY_PROGRAM_ID
|
override val contract = ANOTHER_DUMMY_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = emptyList()
|
get() = emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package net.corda.contracts;
|
package net.corda.contracts;
|
||||||
|
|
||||||
import net.corda.core.contracts.*;
|
import net.corda.core.contracts.Amount;
|
||||||
|
import net.corda.core.contracts.ContractState;
|
||||||
|
import net.corda.core.contracts.Issued;
|
||||||
|
import net.corda.core.contracts.PartyAndReference;
|
||||||
|
import net.corda.core.crypto.PublicKeyTree;
|
||||||
|
|
||||||
import java.security.*;
|
import java.time.Instant;
|
||||||
import java.time.*;
|
import java.util.Currency;
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/* This is an interface solely created to demonstrate that the same kotlin tests can be run against
|
/* This is an interface solely created to demonstrate that the same kotlin tests can be run against
|
||||||
* either a Java implementation of the CommercialPaper or a kotlin implementation.
|
* either a Java implementation of the CommercialPaper or a kotlin implementation.
|
||||||
@ -12,7 +15,7 @@ import java.util.*;
|
|||||||
* ultimately either language can be used against a common test framework (and therefore can be used for real).
|
* ultimately either language can be used against a common test framework (and therefore can be used for real).
|
||||||
*/
|
*/
|
||||||
public interface ICommercialPaperState extends ContractState {
|
public interface ICommercialPaperState extends ContractState {
|
||||||
ICommercialPaperState withOwner(PublicKey newOwner);
|
ICommercialPaperState withOwner(PublicKeyTree newOwner);
|
||||||
|
|
||||||
ICommercialPaperState withIssuance(PartyAndReference newIssuance);
|
ICommercialPaperState withIssuance(PartyAndReference newIssuance);
|
||||||
|
|
||||||
|
@ -1,24 +1,34 @@
|
|||||||
package net.corda.contracts;
|
package net.corda.contracts;
|
||||||
|
|
||||||
import com.google.common.collect.*;
|
import com.google.common.collect.ImmutableList;
|
||||||
import net.corda.contracts.asset.*;
|
import kotlin.Pair;
|
||||||
|
import kotlin.Unit;
|
||||||
|
import net.corda.contracts.asset.CashKt;
|
||||||
import net.corda.core.contracts.*;
|
import net.corda.core.contracts.*;
|
||||||
import net.corda.core.contracts.Timestamp;
|
import net.corda.core.contracts.TransactionForContract.InOutGroup;
|
||||||
import net.corda.core.contracts.TransactionForContract.*;
|
import net.corda.core.contracts.clauses.AnyComposition;
|
||||||
import net.corda.core.contracts.clauses.*;
|
import net.corda.core.contracts.clauses.Clause;
|
||||||
import net.corda.core.node.services.*;
|
import net.corda.core.contracts.clauses.ClauseVerifier;
|
||||||
import net.corda.core.transactions.*;
|
import net.corda.core.contracts.clauses.GroupClauseVerifier;
|
||||||
import kotlin.*;
|
import net.corda.core.crypto.CryptoUtilitiesKt;
|
||||||
import net.corda.core.crypto.*;
|
import net.corda.core.crypto.Party;
|
||||||
import org.jetbrains.annotations.*;
|
import net.corda.core.crypto.PublicKeyTree;
|
||||||
|
import net.corda.core.crypto.SecureHash;
|
||||||
|
import net.corda.core.node.services.VaultService;
|
||||||
|
import net.corda.core.transactions.TransactionBuilder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.security.*;
|
import java.time.Instant;
|
||||||
import java.time.*;
|
import java.util.Collections;
|
||||||
import java.util.*;
|
import java.util.Currency;
|
||||||
import java.util.stream.*;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static net.corda.core.contracts.ContractsDSL.*;
|
import static kotlin.collections.CollectionsKt.single;
|
||||||
import static kotlin.collections.CollectionsKt.*;
|
import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
|
||||||
|
import static net.corda.core.contracts.ContractsDSL.requireThat;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,14 +40,14 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
|
|
||||||
public static class State implements OwnableState, ICommercialPaperState {
|
public static class State implements OwnableState, ICommercialPaperState {
|
||||||
private PartyAndReference issuance;
|
private PartyAndReference issuance;
|
||||||
private PublicKey owner;
|
private PublicKeyTree owner;
|
||||||
private Amount<Issued<Currency>> faceValue;
|
private Amount<Issued<Currency>> faceValue;
|
||||||
private Instant maturityDate;
|
private Instant maturityDate;
|
||||||
|
|
||||||
public State() {
|
public State() {
|
||||||
} // For serialization
|
} // For serialization
|
||||||
|
|
||||||
public State(PartyAndReference issuance, PublicKey owner, Amount<Issued<Currency>> faceValue,
|
public State(PartyAndReference issuance, PublicKeyTree owner, Amount<Issued<Currency>> faceValue,
|
||||||
Instant maturityDate) {
|
Instant maturityDate) {
|
||||||
this.issuance = issuance;
|
this.issuance = issuance;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
@ -49,13 +59,13 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
return new State(this.issuance, this.owner, this.faceValue, this.maturityDate);
|
return new State(this.issuance, this.owner, this.faceValue, this.maturityDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommercialPaperState withOwner(PublicKey newOwner) {
|
public ICommercialPaperState withOwner(PublicKeyTree newOwner) {
|
||||||
return new State(this.issuance, newOwner, this.faceValue, this.maturityDate);
|
return new State(this.issuance, newOwner, this.faceValue, this.maturityDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Pair<CommandData, OwnableState> withNewOwner(@NotNull PublicKey newOwner) {
|
public Pair<CommandData, OwnableState> withNewOwner(@NotNull PublicKeyTree newOwner) {
|
||||||
return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
|
return new Pair<>(new Commands.Move(), new State(this.issuance, newOwner, this.faceValue, this.maturityDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +86,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public PublicKey getOwner() {
|
public PublicKeyTree getOwner() {
|
||||||
return owner;
|
return owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,12 +127,12 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public State withoutOwner() {
|
public State withoutOwner() {
|
||||||
return new State(issuance, NullPublicKey.INSTANCE, faceValue, maturityDate);
|
return new State(issuance, CryptoUtilitiesKt.getNullPublicKeyTree(), faceValue, maturityDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<PublicKey> getParticipants() {
|
public List<PublicKeyTree> getParticipants() {
|
||||||
return ImmutableList.of(this.owner);
|
return ImmutableList.of(this.owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +321,7 @@ public class JavaCommercialPaper implements Contract {
|
|||||||
tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner()));
|
tx.addCommand(new Command(new Commands.Redeem(), paper.getState().getData().getOwner()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKey newOwner) {
|
public void generateMove(TransactionBuilder tx, StateAndRef<State> paper, PublicKeyTree newOwner) {
|
||||||
tx.addInputState(paper);
|
tx.addInputState(paper);
|
||||||
tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary()));
|
tx.addOutputState(new TransactionState<>(new State(paper.getState().getData().getIssuance(), newOwner, paper.getState().getData().getFaceValue(), paper.getState().getData().getMaturityDate()), paper.getState().getNotary()));
|
||||||
tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner()));
|
tx.addCommand(new Command(new Commands.Move(), paper.getState().getData().getOwner()));
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
package net.corda.contracts
|
package net.corda.contracts
|
||||||
|
|
||||||
import net.corda.contracts.asset.Cash
|
|
||||||
import net.corda.core.contracts.FungibleAsset
|
|
||||||
import net.corda.core.contracts.InsufficientBalanceException
|
|
||||||
import net.corda.contracts.asset.sumCashBy
|
import net.corda.contracts.asset.sumCashBy
|
||||||
import net.corda.contracts.clause.AbstractIssue
|
import net.corda.contracts.clause.AbstractIssue
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
@ -11,9 +8,8 @@ import net.corda.core.contracts.clauses.Clause
|
|||||||
import net.corda.core.contracts.clauses.GroupClauseVerifier
|
import net.corda.core.contracts.clauses.GroupClauseVerifier
|
||||||
import net.corda.core.contracts.clauses.verifyClause
|
import net.corda.core.contracts.clauses.verifyClause
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toBase58String
|
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.random63BitValue
|
import net.corda.core.random63BitValue
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
@ -22,7 +18,6 @@ import net.corda.core.schemas.QueryableState
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.Emoji
|
import net.corda.core.utilities.Emoji
|
||||||
import net.corda.schemas.CommercialPaperSchemaV1
|
import net.corda.schemas.CommercialPaperSchemaV1
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -64,22 +59,22 @@ class CommercialPaper : Contract {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val issuance: PartyAndReference,
|
val issuance: PartyAndReference,
|
||||||
override val owner: PublicKey,
|
override val owner: PublicKeyTree,
|
||||||
val faceValue: Amount<Issued<Currency>>,
|
val faceValue: Amount<Issued<Currency>>,
|
||||||
val maturityDate: Instant
|
val maturityDate: Instant
|
||||||
) : OwnableState, QueryableState, ICommercialPaperState {
|
) : OwnableState, QueryableState, ICommercialPaperState {
|
||||||
override val contract = CP_PROGRAM_ID
|
override val contract = CP_PROGRAM_ID
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = listOf(owner)
|
get() = listOf(owner)
|
||||||
|
|
||||||
val token: Issued<Terms>
|
val token: Issued<Terms>
|
||||||
get() = Issued(issuance, Terms(faceValue.token, maturityDate))
|
get() = Issued(issuance, Terms(faceValue.token, maturityDate))
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by ${owner.toStringShort()})"
|
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
|
||||||
|
|
||||||
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
||||||
override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner)
|
override fun withOwner(newOwner: PublicKeyTree): ICommercialPaperState = copy(owner = newOwner)
|
||||||
|
|
||||||
override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance)
|
override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance)
|
||||||
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
||||||
@ -205,7 +200,7 @@ class CommercialPaper : Contract {
|
|||||||
/**
|
/**
|
||||||
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
* Updates the given partial transaction with an input/output/command to reassign ownership of the paper.
|
||||||
*/
|
*/
|
||||||
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
|
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKeyTree) {
|
||||||
tx.addInputState(paper)
|
tx.addInputState(paper)
|
||||||
tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary))
|
tx.addOutputState(TransactionState(paper.state.data.copy(owner = newOwner), paper.state.notary))
|
||||||
tx.addCommand(Commands.Move(), paper.state.data.owner)
|
tx.addCommand(Commands.Move(), paper.state.data.owner)
|
||||||
@ -228,8 +223,8 @@ class CommercialPaper : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
infix fun CommercialPaper.State.`owned by`(owner: PublicKey) = copy(owner = owner)
|
infix fun CommercialPaper.State.`owned by`(owner: PublicKeyTree) = copy(owner = owner)
|
||||||
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
|
infix fun CommercialPaper.State.`with notary`(notary: Party) = TransactionState(this, notary)
|
||||||
infix fun ICommercialPaperState.`owned by`(newOwner: PublicKey) = withOwner(newOwner)
|
infix fun ICommercialPaperState.`owned by`(newOwner: PublicKeyTree) = withOwner(newOwner)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
package net.corda.contracts
|
package net.corda.contracts
|
||||||
|
|
||||||
import net.corda.contracts.asset.Cash
|
|
||||||
import net.corda.core.contracts.InsufficientBalanceException
|
|
||||||
import net.corda.contracts.asset.sumCashBy
|
import net.corda.contracts.asset.sumCashBy
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.NullPublicKey
|
import net.corda.core.crypto.NullPublicKeyTree
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.node.services.Vault
|
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.Emoji
|
import net.corda.core.utilities.Emoji
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -30,19 +26,19 @@ class CommercialPaperLegacy : Contract {
|
|||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
val issuance: PartyAndReference,
|
val issuance: PartyAndReference,
|
||||||
override val owner: PublicKey,
|
override val owner: PublicKeyTree,
|
||||||
val faceValue: Amount<Issued<Currency>>,
|
val faceValue: Amount<Issued<Currency>>,
|
||||||
val maturityDate: Instant
|
val maturityDate: Instant
|
||||||
) : OwnableState, ICommercialPaperState {
|
) : OwnableState, ICommercialPaperState {
|
||||||
override val contract = CP_LEGACY_PROGRAM_ID
|
override val contract = CP_LEGACY_PROGRAM_ID
|
||||||
override val participants = listOf(owner)
|
override val participants = listOf(owner)
|
||||||
|
|
||||||
fun withoutOwner() = copy(owner = NullPublicKey)
|
fun withoutOwner() = copy(owner = NullPublicKeyTree)
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by ${owner.toStringShort()})"
|
override fun toString() = "${Emoji.newspaper}CommercialPaper(of $faceValue redeemable on $maturityDate by '$issuance', owned by $owner)"
|
||||||
|
|
||||||
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
// Although kotlin is smart enough not to need these, as we are using the ICommercialPaperState, we need to declare them explicitly for use later,
|
||||||
override fun withOwner(newOwner: PublicKey): ICommercialPaperState = copy(owner = newOwner)
|
override fun withOwner(newOwner: PublicKeyTree): ICommercialPaperState = copy(owner = newOwner)
|
||||||
|
|
||||||
override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance)
|
override fun withIssuance(newIssuance: PartyAndReference): ICommercialPaperState = copy(issuance = newIssuance)
|
||||||
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
override fun withFaceValue(newFaceValue: Amount<Issued<Currency>>): ICommercialPaperState = copy(faceValue = newFaceValue)
|
||||||
@ -119,7 +115,7 @@ class CommercialPaperLegacy : Contract {
|
|||||||
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
|
return TransactionBuilder(notary = notary).withItems(state, Command(Commands.Issue(), issuance.party.owningKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKey) {
|
fun generateMove(tx: TransactionBuilder, paper: StateAndRef<State>, newOwner: PublicKeyTree) {
|
||||||
tx.addInputState(paper)
|
tx.addInputState(paper)
|
||||||
tx.addOutputState(paper.state.data.withOwner(newOwner))
|
tx.addOutputState(paper.state.data.withOwner(newOwner))
|
||||||
tx.addCommand(Command(Commands.Move(), paper.state.data.owner))
|
tx.addCommand(Command(Commands.Move(), paper.state.data.owner))
|
||||||
|
@ -9,16 +9,13 @@ import net.corda.core.contracts.clauses.FirstComposition
|
|||||||
import net.corda.core.contracts.clauses.GroupClauseVerifier
|
import net.corda.core.contracts.clauses.GroupClauseVerifier
|
||||||
import net.corda.core.contracts.clauses.verifyClause
|
import net.corda.core.contracts.clauses.verifyClause
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.node.services.Vault
|
|
||||||
import net.corda.core.schemas.MappedSchema
|
import net.corda.core.schemas.MappedSchema
|
||||||
import net.corda.core.schemas.PersistentState
|
import net.corda.core.schemas.PersistentState
|
||||||
import net.corda.core.schemas.QueryableState
|
import net.corda.core.schemas.QueryableState
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.Emoji
|
import net.corda.core.utilities.Emoji
|
||||||
import net.corda.schemas.CashSchemaV1
|
import net.corda.schemas.CashSchemaV1
|
||||||
import net.corda.core.crypto.*
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -85,22 +82,22 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
|
|||||||
override val amount: Amount<Issued<Currency>>,
|
override val amount: Amount<Issued<Currency>>,
|
||||||
|
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount. */
|
/** There must be a MoveCommand signed by this key to claim the amount. */
|
||||||
override val owner: PublicKey,
|
override val owner: PublicKeyTree,
|
||||||
override val encumbrance: Int? = null
|
override val encumbrance: Int? = null
|
||||||
) : FungibleAsset<Currency>, QueryableState {
|
) : FungibleAsset<Currency>, QueryableState {
|
||||||
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKey)
|
constructor(deposit: PartyAndReference, amount: Amount<Currency>, owner: PublicKeyTree)
|
||||||
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
||||||
|
|
||||||
override val exitKeys = setOf(owner, amount.token.issuer.party.owningKey)
|
override val exitKeys = setOf(owner, amount.token.issuer.party.owningKey)
|
||||||
override val contract = CASH_PROGRAM_ID
|
override val contract = CASH_PROGRAM_ID
|
||||||
override val participants = listOf(owner)
|
override val participants = listOf(owner)
|
||||||
|
|
||||||
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKey): FungibleAsset<Currency>
|
override fun move(newAmount: Amount<Issued<Currency>>, newOwner: PublicKeyTree): FungibleAsset<Currency>
|
||||||
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
|
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
|
||||||
|
|
||||||
override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by ${owner.toStringShort()})"
|
override fun toString() = "${Emoji.bagOfCash}Cash($amount at ${amount.token.issuer} owned by $owner)"
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
|
|
||||||
/** Object Relational Mapping support. */
|
/** Object Relational Mapping support. */
|
||||||
override fun generateMappedObject(schema: MappedSchema): PersistentState {
|
override fun generateMappedObject(schema: MappedSchema): PersistentState {
|
||||||
@ -148,13 +145,13 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
|
|||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKey, notary: Party)
|
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Currency>, pennies: Long, owner: PublicKeyTree, notary: Party)
|
||||||
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKey, notary: Party) {
|
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Currency>>, owner: PublicKeyTree, notary: Party) {
|
||||||
check(tx.inputStates().isEmpty())
|
check(tx.inputStates().isEmpty())
|
||||||
check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
|
check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
|
||||||
val at = amount.token.issuer
|
val at = amount.token.issuer
|
||||||
@ -162,7 +159,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
|
|||||||
tx.addCommand(generateIssueCommand(), at.party.owningKey)
|
tx.addCommand(generateIssueCommand(), at.party.owningKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
|
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Currency>>, owner: PublicKeyTree)
|
||||||
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
||||||
override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount)
|
override fun generateExitCommand(amount: Amount<Issued<Currency>>) = Commands.Exit(amount)
|
||||||
override fun generateIssueCommand() = Commands.Issue()
|
override fun generateIssueCommand() = Commands.Issue()
|
||||||
@ -179,7 +176,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
|
|||||||
* if there are none, or if any of the cash states cannot be added together (i.e. are
|
* if there are none, or if any of the cash states cannot be added together (i.e. are
|
||||||
* different currencies or issuers).
|
* different currencies or issuers).
|
||||||
*/
|
*/
|
||||||
fun Iterable<ContractState>.sumCashBy(owner: PublicKey): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
|
fun Iterable<ContractState>.sumCashBy(owner: PublicKeyTree): Amount<Issued<Currency>> = filterIsInstance<Cash.State>().filter { it.owner == owner }.map { it.amount }.sumOrThrow()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
|
* Sums the cash states in the list, throwing an exception if there are none, or if any of the cash
|
||||||
@ -195,12 +192,12 @@ fun Iterable<ContractState>.sumCashOrZero(currency: Issued<Currency>): Amount<Is
|
|||||||
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
|
return filterIsInstance<Cash.State>().map { it.amount }.sumOrZero(currency)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Cash.State.ownedBy(owner: PublicKey) = copy(owner = owner)
|
fun Cash.State.ownedBy(owner: PublicKeyTree) = copy(owner = owner)
|
||||||
fun Cash.State.issuedBy(party: Party) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party))))
|
fun Cash.State.issuedBy(party: Party) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = amount.token.issuer.copy(party = party))))
|
||||||
fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit)))
|
fun Cash.State.issuedBy(deposit: PartyAndReference) = copy(amount = Amount(amount.quantity, amount.token.copy(issuer = deposit)))
|
||||||
fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))
|
fun Cash.State.withDeposit(deposit: PartyAndReference): Cash.State = copy(amount = amount.copy(token = amount.token.copy(issuer = deposit)))
|
||||||
|
|
||||||
infix fun Cash.State.`owned by`(owner: PublicKey) = ownedBy(owner)
|
infix fun Cash.State.`owned by`(owner: PublicKeyTree) = ownedBy(owner)
|
||||||
infix fun Cash.State.`issued by`(party: Party) = issuedBy(party)
|
infix fun Cash.State.`issued by`(party: Party) = issuedBy(party)
|
||||||
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
|
infix fun Cash.State.`issued by`(deposit: PartyAndReference) = issuedBy(deposit)
|
||||||
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)
|
infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = withDeposit(deposit)
|
||||||
@ -210,8 +207,8 @@ infix fun Cash.State.`with deposit`(deposit: PartyAndReference): Cash.State = wi
|
|||||||
/** A randomly generated key. */
|
/** A randomly generated key. */
|
||||||
val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
|
val DUMMY_CASH_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
|
||||||
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
|
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
|
||||||
val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public).ref(1) }
|
val DUMMY_CASH_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_CASH_ISSUER_KEY.public.tree).ref(1) }
|
||||||
/** An extension property that lets you write 100.DOLLARS.CASH */
|
/** An extension property that lets you write 100.DOLLARS.CASH */
|
||||||
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKey)
|
val Amount<Currency>.CASH: Cash.State get() = Cash.State(Amount(quantity, Issued(DUMMY_CASH_ISSUER, token)), NullPublicKeyTree)
|
||||||
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */
|
/** An extension property that lets you get a cash state from an issued token, under the [NullPublicKey] */
|
||||||
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKey)
|
val Amount<Issued<Currency>>.STATE: Cash.State get() = Cash.State(this, NullPublicKeyTree)
|
||||||
|
@ -4,15 +4,14 @@ import net.corda.contracts.clause.AbstractConserveAmount
|
|||||||
import net.corda.contracts.clause.AbstractIssue
|
import net.corda.contracts.clause.AbstractIssue
|
||||||
import net.corda.contracts.clause.NoZeroSizedOutputs
|
import net.corda.contracts.clause.NoZeroSizedOutputs
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.GroupClauseVerifier
|
|
||||||
import net.corda.core.contracts.clauses.AnyComposition
|
import net.corda.core.contracts.clauses.AnyComposition
|
||||||
|
import net.corda.core.contracts.clauses.GroupClauseVerifier
|
||||||
import net.corda.core.contracts.clauses.verifyClause
|
import net.corda.core.contracts.clauses.verifyClause
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.newSecureRandom
|
import net.corda.core.crypto.newSecureRandom
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -96,21 +95,21 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
|
|||||||
override val amount: Amount<Issued<Commodity>>,
|
override val amount: Amount<Issued<Commodity>>,
|
||||||
|
|
||||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||||
override val owner: PublicKey
|
override val owner: PublicKeyTree
|
||||||
) : FungibleAsset<Commodity> {
|
) : FungibleAsset<Commodity> {
|
||||||
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKey)
|
constructor(deposit: PartyAndReference, amount: Amount<Commodity>, owner: PublicKeyTree)
|
||||||
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
: this(Amount(amount.quantity, Issued(deposit, amount.token)), owner)
|
||||||
|
|
||||||
override val contract = COMMODITY_PROGRAM_ID
|
override val contract = COMMODITY_PROGRAM_ID
|
||||||
override val exitKeys = Collections.singleton(owner)
|
override val exitKeys = Collections.singleton(owner)
|
||||||
override val participants = listOf(owner)
|
override val participants = listOf(owner)
|
||||||
|
|
||||||
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKey): FungibleAsset<Commodity>
|
override fun move(newAmount: Amount<Issued<Commodity>>, newOwner: PublicKeyTree): FungibleAsset<Commodity>
|
||||||
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
|
= copy(amount = amount.copy(newAmount.quantity, amount.token), owner = newOwner)
|
||||||
|
|
||||||
override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by ${owner.toStringShort()})"
|
override fun toString() = "Commodity($amount at ${amount.token.issuer} owned by $owner)"
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just for grouping
|
// Just for grouping
|
||||||
@ -144,13 +143,13 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
|
|||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKey, notary: Party)
|
fun generateIssue(tx: TransactionBuilder, tokenDef: Issued<Commodity>, pennies: Long, owner: PublicKeyTree, notary: Party)
|
||||||
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
= generateIssue(tx, Amount(pennies, tokenDef), owner, notary)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
||||||
*/
|
*/
|
||||||
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKey, notary: Party) {
|
fun generateIssue(tx: TransactionBuilder, amount: Amount<Issued<Commodity>>, owner: PublicKeyTree, notary: Party) {
|
||||||
check(tx.inputStates().isEmpty())
|
check(tx.inputStates().isEmpty())
|
||||||
check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
|
check(tx.outputStates().map { it.data }.sumCashOrNull() == null)
|
||||||
val at = amount.token.issuer
|
val at = amount.token.issuer
|
||||||
@ -159,7 +158,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKey)
|
override fun deriveState(txState: TransactionState<State>, amount: Amount<Issued<Commodity>>, owner: PublicKeyTree)
|
||||||
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
||||||
override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount)
|
override fun generateExitCommand(amount: Amount<Issued<Commodity>>) = Commands.Exit(amount)
|
||||||
override fun generateIssueCommand() = Commands.Issue()
|
override fun generateIssueCommand() = Commands.Issue()
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
package net.corda.contracts.asset
|
package net.corda.contracts.asset
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting
|
import com.google.common.annotations.VisibleForTesting
|
||||||
|
import net.corda.contracts.asset.Obligation.Lifecycle.NORMAL
|
||||||
import net.corda.contracts.clause.*
|
import net.corda.contracts.clause.*
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.*
|
import net.corda.core.contracts.clauses.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.random63BitValue
|
import net.corda.core.random63BitValue
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.Emoji
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.utilities.NonEmptySet
|
||||||
|
import net.corda.core.utilities.TEST_TX_TIME
|
||||||
|
import net.corda.core.utilities.nonEmptySetOf
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -267,21 +269,21 @@ class Obligation<P> : Contract {
|
|||||||
val template: Terms<P>,
|
val template: Terms<P>,
|
||||||
val quantity: Long,
|
val quantity: Long,
|
||||||
/** The public key of the entity the contract pays to */
|
/** The public key of the entity the contract pays to */
|
||||||
val beneficiary: PublicKey
|
val beneficiary: PublicKeyTree
|
||||||
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
|
) : FungibleAsset<Obligation.Terms<P>>, NettableState<State<P>, MultilateralNetState<P>> {
|
||||||
override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template))
|
override val amount: Amount<Issued<Terms<P>>> = Amount(quantity, Issued(obligor.ref(0), template))
|
||||||
override val contract = OBLIGATION_PROGRAM_ID
|
override val contract = OBLIGATION_PROGRAM_ID
|
||||||
override val exitKeys: Collection<PublicKey> = setOf(beneficiary)
|
override val exitKeys: Collection<PublicKeyTree> = setOf(beneficiary)
|
||||||
val dueBefore: Instant = template.dueBefore
|
val dueBefore: Instant = template.dueBefore
|
||||||
override val participants: List<PublicKey> = listOf(obligor.owningKey, beneficiary)
|
override val participants: List<PublicKeyTree> = listOf(obligor.owningKey, beneficiary)
|
||||||
override val owner: PublicKey = beneficiary
|
override val owner: PublicKeyTree = beneficiary
|
||||||
|
|
||||||
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKey): State<P>
|
override fun move(newAmount: Amount<Issued<Terms<P>>>, newOwner: PublicKeyTree): State<P>
|
||||||
= copy(quantity = newAmount.quantity, beneficiary = newOwner)
|
= copy(quantity = newAmount.quantity, beneficiary = newOwner)
|
||||||
|
|
||||||
override fun toString() = when (lifecycle) {
|
override fun toString() = when (lifecycle) {
|
||||||
Lifecycle.NORMAL -> "${Emoji.bagOfCash}Debt($amount due $dueBefore to ${beneficiary.toStringShort()})"
|
Lifecycle.NORMAL -> "${Emoji.bagOfCash}Debt($amount due $dueBefore to $beneficiary)"
|
||||||
Lifecycle.DEFAULTED -> "${Emoji.bagOfCash}Debt($amount unpaid by $dueBefore to ${beneficiary.toStringShort()})"
|
Lifecycle.DEFAULTED -> "${Emoji.bagOfCash}Debt($amount unpaid by $dueBefore to $beneficiary)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val bilateralNetState: BilateralNetState<P>
|
override val bilateralNetState: BilateralNetState<P>
|
||||||
@ -309,7 +311,7 @@ class Obligation<P> : Contract {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(beneficiary = newOwner))
|
override fun withNewOwner(newOwner: PublicKeyTree) = Pair(Commands.Move(), copy(beneficiary = newOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just for grouping
|
// Just for grouping
|
||||||
@ -414,7 +416,7 @@ class Obligation<P> : Contract {
|
|||||||
* and same parties involved).
|
* and same parties involved).
|
||||||
*/
|
*/
|
||||||
fun generateCloseOutNetting(tx: TransactionBuilder,
|
fun generateCloseOutNetting(tx: TransactionBuilder,
|
||||||
signer: PublicKey,
|
signer: PublicKeyTree,
|
||||||
vararg states: State<P>) {
|
vararg states: State<P>) {
|
||||||
val netState = states.firstOrNull()?.bilateralNetState
|
val netState = states.firstOrNull()?.bilateralNetState
|
||||||
|
|
||||||
@ -442,7 +444,7 @@ class Obligation<P> : Contract {
|
|||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<Terms<P>>>,
|
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<Terms<P>>>,
|
||||||
assetStates: List<StateAndRef<Obligation.State<P>>>): PublicKey
|
assetStates: List<StateAndRef<Obligation.State<P>>>): PublicKeyTree
|
||||||
= Clauses.ConserveAmount<P>().generateExit(tx, amountIssued, assetStates,
|
= Clauses.ConserveAmount<P>().generateExit(tx, amountIssued, assetStates,
|
||||||
deriveState = { state, amount, owner -> state.copy(data = state.data.move(amount, owner)) },
|
deriveState = { state, amount, owner -> state.copy(data = state.data.move(amount, owner)) },
|
||||||
generateMoveCommand = { -> Commands.Move() },
|
generateMoveCommand = { -> Commands.Move() },
|
||||||
@ -456,7 +458,7 @@ class Obligation<P> : Contract {
|
|||||||
obligor: Party,
|
obligor: Party,
|
||||||
issuanceDef: Terms<P>,
|
issuanceDef: Terms<P>,
|
||||||
pennies: Long,
|
pennies: Long,
|
||||||
beneficiary: PublicKey,
|
beneficiary: PublicKeyTree,
|
||||||
notary: Party) {
|
notary: Party) {
|
||||||
check(tx.inputStates().isEmpty())
|
check(tx.inputStates().isEmpty())
|
||||||
check(tx.outputStates().map { it.data }.sumObligationsOrNull<P>() == null)
|
check(tx.outputStates().map { it.data }.sumObligationsOrNull<P>() == null)
|
||||||
@ -472,7 +474,7 @@ class Obligation<P> : Contract {
|
|||||||
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL })
|
"all states are in the normal lifecycle state " by (states.all { it.lifecycle == Lifecycle.NORMAL })
|
||||||
}
|
}
|
||||||
val groups = states.groupBy { it.multilateralNetState }
|
val groups = states.groupBy { it.multilateralNetState }
|
||||||
val partyLookup = HashMap<PublicKey, Party>()
|
val partyLookup = HashMap<PublicKeyTree, Party>()
|
||||||
val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).toSet()
|
val signers = states.map { it.beneficiary }.union(states.map { it.obligor.owningKey }).toSet()
|
||||||
|
|
||||||
// Create a lookup table of the party that each public key represents.
|
// Create a lookup table of the party that each public key represents.
|
||||||
@ -516,7 +518,7 @@ class Obligation<P> : Contract {
|
|||||||
// Produce a new set of states
|
// Produce a new set of states
|
||||||
val groups = statesAndRefs.groupBy { it.state.data.amount.token }
|
val groups = statesAndRefs.groupBy { it.state.data.amount.token }
|
||||||
for ((aggregateState, stateAndRefs) in groups) {
|
for ((aggregateState, stateAndRefs) in groups) {
|
||||||
val partiesUsed = ArrayList<PublicKey>()
|
val partiesUsed = ArrayList<PublicKeyTree>()
|
||||||
stateAndRefs.forEach { stateAndRef ->
|
stateAndRefs.forEach { stateAndRef ->
|
||||||
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
|
val outState = stateAndRef.state.data.copy(lifecycle = lifecycle)
|
||||||
tx.addInputState(stateAndRef)
|
tx.addInputState(stateAndRef)
|
||||||
@ -561,7 +563,7 @@ class Obligation<P> : Contract {
|
|||||||
val template: Terms<P> = issuanceDef.product
|
val template: Terms<P> = issuanceDef.product
|
||||||
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
|
val obligationTotal: Amount<P> = Amount(states.map { it.data }.sumObligations<P>().quantity, template.product)
|
||||||
var obligationRemaining: Amount<P> = obligationTotal
|
var obligationRemaining: Amount<P> = obligationTotal
|
||||||
val assetSigners = HashSet<PublicKey>()
|
val assetSigners = HashSet<PublicKeyTree>()
|
||||||
|
|
||||||
statesAndRefs.forEach { tx.addInputState(it) }
|
statesAndRefs.forEach { tx.addInputState(it) }
|
||||||
|
|
||||||
@ -613,8 +615,8 @@ class Obligation<P> : Contract {
|
|||||||
*
|
*
|
||||||
* @return a map of obligor/beneficiary pairs to the balance due.
|
* @return a map of obligor/beneficiary pairs to the balance due.
|
||||||
*/
|
*/
|
||||||
fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>> {
|
fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligation.State<P>>): Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<Obligation.Terms<P>>> {
|
||||||
val balances = HashMap<Pair<PublicKey, PublicKey>, Amount<Obligation.Terms<P>>>()
|
val balances = HashMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<Obligation.Terms<P>>>()
|
||||||
|
|
||||||
states.forEach { state ->
|
states.forEach { state ->
|
||||||
val key = Pair(state.obligor.owningKey, state.beneficiary)
|
val key = Pair(state.obligor.owningKey, state.beneficiary)
|
||||||
@ -628,8 +630,8 @@ fun <P> extractAmountsDue(product: Obligation.Terms<P>, states: Iterable<Obligat
|
|||||||
/**
|
/**
|
||||||
* Net off the amounts due between parties.
|
* Net off the amounts due between parties.
|
||||||
*/
|
*/
|
||||||
fun <P> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<Pair<PublicKey, PublicKey>, Amount<P>> {
|
fun <P> netAmountsDue(balances: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>): Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>> {
|
||||||
val nettedBalances = HashMap<Pair<PublicKey, PublicKey>, Amount<P>>()
|
val nettedBalances = HashMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>()
|
||||||
|
|
||||||
balances.forEach { balance ->
|
balances.forEach { balance ->
|
||||||
val (obligor, beneficiary) = balance.key
|
val (obligor, beneficiary) = balance.key
|
||||||
@ -653,8 +655,8 @@ fun <P> netAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map
|
|||||||
* @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being
|
* @param balances payments due, indexed by obligor and beneficiary. Zero balances are stripped from the map before being
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
fun <P> sumAmountsDue(balances: Map<Pair<PublicKey, PublicKey>, Amount<P>>): Map<PublicKey, Long> {
|
fun <P> sumAmountsDue(balances: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<P>>): Map<PublicKeyTree, Long> {
|
||||||
val sum = HashMap<PublicKey, Long>()
|
val sum = HashMap<PublicKeyTree, Long>()
|
||||||
|
|
||||||
// Fill the map with zeroes initially
|
// Fill the map with zeroes initially
|
||||||
balances.keys.forEach {
|
balances.keys.forEach {
|
||||||
@ -695,19 +697,19 @@ fun <P> Iterable<ContractState>.sumObligationsOrZero(issuanceDef: Issued<Obligat
|
|||||||
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
|
= filterIsInstance<Obligation.State<P>>().filter { it.lifecycle == Obligation.Lifecycle.NORMAL }.map { it.amount }.sumOrZero(issuanceDef)
|
||||||
|
|
||||||
infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
|
infix fun <T> Obligation.State<T>.at(dueBefore: Instant) = copy(template = template.copy(dueBefore = dueBefore))
|
||||||
infix fun <T> Obligation.State<T>.between(parties: Pair<Party, PublicKey>) = copy(obligor = parties.first, beneficiary = parties.second)
|
infix fun <T> Obligation.State<T>.between(parties: Pair<Party, PublicKeyTree>) = copy(obligor = parties.first, beneficiary = parties.second)
|
||||||
infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKey) = copy(beneficiary = owner)
|
infix fun <T> Obligation.State<T>.`owned by`(owner: PublicKeyTree) = copy(beneficiary = owner)
|
||||||
infix fun <T> Obligation.State<T>.`issued by`(party: Party) = copy(obligor = party)
|
infix fun <T> Obligation.State<T>.`issued by`(party: Party) = copy(obligor = party)
|
||||||
// For Java users:
|
// For Java users:
|
||||||
@Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: PublicKey) = copy(beneficiary = owner)
|
@Suppress("unused") fun <T> Obligation.State<T>.ownedBy(owner: PublicKeyTree) = copy(beneficiary = owner)
|
||||||
@Suppress("unused") fun <T> Obligation.State<T>.issuedBy(party: Party) = copy(obligor = party)
|
@Suppress("unused") fun <T> Obligation.State<T>.issuedBy(party: Party) = copy(obligor = party)
|
||||||
|
|
||||||
/** A randomly generated key. */
|
/** A randomly generated key. */
|
||||||
val DUMMY_OBLIGATION_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
|
val DUMMY_OBLIGATION_ISSUER_KEY by lazy { entropyToKeyPair(BigInteger.valueOf(10)) }
|
||||||
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
|
/** A dummy, randomly generated issuer party by the name of "Snake Oil Issuer" */
|
||||||
val DUMMY_OBLIGATION_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_OBLIGATION_ISSUER_KEY.public) }
|
val DUMMY_OBLIGATION_ISSUER by lazy { Party("Snake Oil Issuer", DUMMY_OBLIGATION_ISSUER_KEY.public.tree) }
|
||||||
|
|
||||||
val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
|
val Issued<Currency>.OBLIGATION_DEF: Obligation.Terms<Currency>
|
||||||
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
|
get() = Obligation.Terms(nonEmptySetOf(Cash().legalContractReference), nonEmptySetOf(this), TEST_TX_TIME)
|
||||||
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
|
val Amount<Issued<Currency>>.OBLIGATION: Obligation.State<Currency>
|
||||||
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NullPublicKey)
|
get() = Obligation.State(Obligation.Lifecycle.NORMAL, DUMMY_OBLIGATION_ISSUER, token.OBLIGATION_DEF, quantity, NullPublicKeyTree)
|
||||||
|
@ -2,9 +2,8 @@ package net.corda.contracts.asset
|
|||||||
|
|
||||||
import net.corda.contracts.clause.AbstractConserveAmount
|
import net.corda.contracts.clause.AbstractConserveAmount
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@ -40,7 +39,7 @@ abstract class OnLedgerAsset<T : Any, C: CommandData, S : FungibleAsset<T>> : Co
|
|||||||
* @return the public key of the assets issuer, who must sign the transaction for it to be valid.
|
* @return the public key of the assets issuer, who must sign the transaction for it to be valid.
|
||||||
*/
|
*/
|
||||||
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
||||||
assetStates: List<StateAndRef<S>>): PublicKey
|
assetStates: List<StateAndRef<S>>): PublicKeyTree
|
||||||
= conserveClause.generateExit(tx, amountIssued, assetStates,
|
= conserveClause.generateExit(tx, amountIssued, assetStates,
|
||||||
deriveState = { state, amount, owner -> deriveState(state, amount, owner) },
|
deriveState = { state, amount, owner -> deriveState(state, amount, owner) },
|
||||||
generateMoveCommand = { -> generateMoveCommand() },
|
generateMoveCommand = { -> generateMoveCommand() },
|
||||||
@ -56,5 +55,5 @@ abstract class OnLedgerAsset<T : Any, C: CommandData, S : FungibleAsset<T>> : Co
|
|||||||
* implementations to have fields in their state which we don't know about here, and we simply leave them untouched
|
* implementations to have fields in their state which we don't know about here, and we simply leave them untouched
|
||||||
* when sending out "change" from spending/exiting.
|
* when sending out "change" from spending/exiting.
|
||||||
*/
|
*/
|
||||||
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKey): TransactionState<S>
|
abstract fun deriveState(txState: TransactionState<S>, amount: Amount<Issued<T>>, owner: PublicKeyTree): TransactionState<S>
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ package net.corda.contracts.clause
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.Clause
|
import net.corda.core.contracts.clauses.Clause
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,9 +47,9 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
|
|||||||
*/
|
*/
|
||||||
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
fun generateExit(tx: TransactionBuilder, amountIssued: Amount<Issued<T>>,
|
||||||
assetStates: List<StateAndRef<S>>,
|
assetStates: List<StateAndRef<S>>,
|
||||||
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKey) -> TransactionState<S>,
|
deriveState: (TransactionState<S>, Amount<Issued<T>>, PublicKeyTree) -> TransactionState<S>,
|
||||||
generateMoveCommand: () -> CommandData,
|
generateMoveCommand: () -> CommandData,
|
||||||
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKey {
|
generateExitCommand: (Amount<Issued<T>>) -> CommandData): PublicKeyTree {
|
||||||
val owner = assetStates.map { it.state.data.owner }.toSet().single()
|
val owner = assetStates.map { it.state.data.owner }.toSet().single()
|
||||||
val currency = amountIssued.token.product
|
val currency = amountIssued.token.product
|
||||||
val amount = Amount(amountIssued.quantity, currency)
|
val amount = Amount(amountIssued.quantity, currency)
|
||||||
@ -92,7 +92,7 @@ abstract class AbstractConserveAmount<S : FungibleAsset<T>, C : CommandData, T :
|
|||||||
val outputAmount: Amount<Issued<T>> = outputs.sumFungibleOrZero(groupingKey)
|
val outputAmount: Amount<Issued<T>> = outputs.sumFungibleOrZero(groupingKey)
|
||||||
|
|
||||||
// If we want to remove assets from the ledger, that must be signed for by the issuer and owner.
|
// If we want to remove assets from the ledger, that must be signed for by the issuer and owner.
|
||||||
val exitKeys: Set<PublicKey> = inputs.flatMap { it.exitKeys }.toSet()
|
val exitKeys: Set<PublicKeyTree> = inputs.flatMap { it.exitKeys }.toSet()
|
||||||
val exitCommand = matchedCommands.select<FungibleAsset.Commands.Exit<T>>(parties = null, signers = exitKeys).filter { it.value.amount.token == groupingKey }.singleOrNull()
|
val exitCommand = matchedCommands.select<FungibleAsset.Commands.Exit<T>>(parties = null, signers = exitKeys).filter { it.value.amount.token == groupingKey }.singleOrNull()
|
||||||
val amountExitingLedger: Amount<Issued<T>> = exitCommand?.value?.amount ?: Amount(0, groupingKey)
|
val amountExitingLedger: Amount<Issued<T>> = exitCommand?.value?.amount ?: Amount(0, groupingKey)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import net.corda.contracts.asset.extractAmountsDue
|
|||||||
import net.corda.contracts.asset.sumAmountsDue
|
import net.corda.contracts.asset.sumAmountsDue
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.contracts.clauses.Clause
|
import net.corda.core.contracts.clauses.Clause
|
||||||
import java.security.PublicKey
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common interface for the state subsets used when determining nettability of two or more states. Exposes the
|
* Common interface for the state subsets used when determining nettability of two or more states. Exposes the
|
||||||
@ -22,7 +22,7 @@ interface NetState<P> {
|
|||||||
* Bilateral states are used in close-out netting.
|
* Bilateral states are used in close-out netting.
|
||||||
*/
|
*/
|
||||||
data class BilateralNetState<P>(
|
data class BilateralNetState<P>(
|
||||||
val partyKeys: Set<PublicKey>,
|
val partyKeys: Set<PublicKeyTree>,
|
||||||
override val template: Obligation.Terms<P>
|
override val template: Obligation.Terms<P>
|
||||||
) : NetState<P>
|
) : NetState<P>
|
||||||
|
|
||||||
|
@ -5,7 +5,10 @@ import com.pholser.junit.quickcheck.generator.Generator
|
|||||||
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
|
import com.pholser.junit.quickcheck.generator.java.util.ArrayListGenerator
|
||||||
import com.pholser.junit.quickcheck.random.SourceOfRandomness
|
import com.pholser.junit.quickcheck.random.SourceOfRandomness
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.Command
|
||||||
|
import net.corda.core.contracts.CommandData
|
||||||
|
import net.corda.core.contracts.ContractState
|
||||||
|
import net.corda.core.contracts.TransactionType
|
||||||
import net.corda.core.crypto.NullSignature
|
import net.corda.core.crypto.NullSignature
|
||||||
import net.corda.core.testing.*
|
import net.corda.core.testing.*
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -22,7 +25,7 @@ class ContractStateGenerator : Generator<ContractState>(ContractState::class.jav
|
|||||||
override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState {
|
override fun generate(random: SourceOfRandomness, status: GenerationStatus): ContractState {
|
||||||
return Cash.State(
|
return Cash.State(
|
||||||
amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status),
|
amount = AmountGenerator(IssuedGenerator(CurrencyGenerator())).generate(random, status),
|
||||||
owner = PublicKeyGenerator().generate(random, status)
|
owner = PublicKeyTreeGenerator().generate(random, status)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,8 +58,8 @@ class CommandDataGenerator : Generator<CommandData>(CommandData::class.java) {
|
|||||||
class CommandGenerator : Generator<Command>(Command::class.java) {
|
class CommandGenerator : Generator<Command>(Command::class.java) {
|
||||||
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Command {
|
override fun generate(random: SourceOfRandomness, status: GenerationStatus): Command {
|
||||||
val signersGenerator = ArrayListGenerator()
|
val signersGenerator = ArrayListGenerator()
|
||||||
signersGenerator.addComponentGenerators(listOf(PublicKeyGenerator()))
|
signersGenerator.addComponentGenerators(listOf(PublicKeyTreeGenerator()))
|
||||||
return Command(CommandDataGenerator().generate(random, status), PublicKeyGenerator().generate(random, status))
|
return Command(CommandDataGenerator().generate(random, status), PublicKeyTreeGenerator().generate(random, status))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,16 +7,15 @@ import net.corda.contracts.asset.DUMMY_CASH_ISSUER_KEY
|
|||||||
import net.corda.core.contracts.Amount
|
import net.corda.core.contracts.Amount
|
||||||
import net.corda.core.contracts.Issued
|
import net.corda.core.contracts.Issued
|
||||||
import net.corda.core.contracts.PartyAndReference
|
import net.corda.core.contracts.PartyAndReference
|
||||||
import net.corda.core.transactions.SignedTransaction
|
|
||||||
import net.corda.core.contracts.TransactionType
|
import net.corda.core.contracts.TransactionType
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.protocols.StateMachineRunId
|
|
||||||
import net.corda.core.serialization.OpaqueBytes
|
import net.corda.core.serialization.OpaqueBytes
|
||||||
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
@ -36,12 +35,12 @@ fun ServiceHub.fillWithSomeTestCash(howMuch: Amount<Currency>,
|
|||||||
atMostThisManyStates: Int = 10,
|
atMostThisManyStates: Int = 10,
|
||||||
rng: Random = Random(),
|
rng: Random = Random(),
|
||||||
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
|
ref: OpaqueBytes = OpaqueBytes(ByteArray(1, { 1 })),
|
||||||
ownedBy: PublicKey? = null,
|
ownedBy: PublicKeyTree? = null,
|
||||||
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
|
issuedBy: PartyAndReference = DUMMY_CASH_ISSUER,
|
||||||
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault {
|
issuerKey: KeyPair = DUMMY_CASH_ISSUER_KEY): Vault {
|
||||||
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
|
val amounts = calculateRandomlySizedAmounts(howMuch, atLeastThisManyStates, atMostThisManyStates, rng)
|
||||||
|
|
||||||
val myKey: PublicKey = ownedBy ?: myInfo.legalIdentity.owningKey
|
val myKey: PublicKeyTree = ownedBy ?: myInfo.legalIdentity.owningKey
|
||||||
|
|
||||||
// We will allocate one state to one transaction, for simplicities sake.
|
// We will allocate one state to one transaction, for simplicities sake.
|
||||||
val cash = Cash()
|
val cash = Cash()
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package net.corda.protocols
|
package net.corda.protocols
|
||||||
|
|
||||||
import co.paralleluniverse.fibers.Suspendable
|
import co.paralleluniverse.fibers.Suspendable
|
||||||
import net.corda.contracts.asset.Cash
|
|
||||||
import net.corda.contracts.asset.sumCashBy
|
import net.corda.contracts.asset.sumCashBy
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.Party
|
|
||||||
import net.corda.core.crypto.signWithECDSA
|
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.services.ServiceType
|
|
||||||
import net.corda.core.protocols.ProtocolLogic
|
import net.corda.core.protocols.ProtocolLogic
|
||||||
import net.corda.core.seconds
|
import net.corda.core.seconds
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -17,7 +13,6 @@ import net.corda.core.transactions.WireTransaction
|
|||||||
import net.corda.core.utilities.ProgressTracker
|
import net.corda.core.utilities.ProgressTracker
|
||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,7 +51,7 @@ object TwoPartyTradeProtocol {
|
|||||||
data class SellerTradeInfo(
|
data class SellerTradeInfo(
|
||||||
val assetForSale: StateAndRef<OwnableState>,
|
val assetForSale: StateAndRef<OwnableState>,
|
||||||
val price: Amount<Currency>,
|
val price: Amount<Currency>,
|
||||||
val sellerOwnerKey: PublicKey
|
val sellerOwnerKey: PublicKeyTree
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey,
|
data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey,
|
||||||
@ -104,8 +99,9 @@ object TwoPartyTradeProtocol {
|
|||||||
private fun receiveAndCheckProposedTransaction(): SignedTransaction {
|
private fun receiveAndCheckProposedTransaction(): SignedTransaction {
|
||||||
progressTracker.currentStep = AWAITING_PROPOSAL
|
progressTracker.currentStep = AWAITING_PROPOSAL
|
||||||
|
|
||||||
|
val myPublicKey = myKeyPair.public.tree
|
||||||
// Make the first message we'll send to kick off the protocol.
|
// Make the first message we'll send to kick off the protocol.
|
||||||
val hello = SellerTradeInfo(assetToSell, price, myKeyPair.public)
|
val hello = SellerTradeInfo(assetToSell, price, myPublicKey)
|
||||||
|
|
||||||
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
|
val maybeSTX = sendAndReceive<SignedTransaction>(otherParty, hello)
|
||||||
|
|
||||||
@ -115,14 +111,14 @@ object TwoPartyTradeProtocol {
|
|||||||
progressTracker.nextStep()
|
progressTracker.nextStep()
|
||||||
|
|
||||||
// Check that the tx proposed by the buyer is valid.
|
// Check that the tx proposed by the buyer is valid.
|
||||||
val wtx: WireTransaction = it.verifySignatures(myKeyPair.public, notaryNode.notaryIdentity.owningKey)
|
val wtx: WireTransaction = it.verifySignatures(myPublicKey, notaryNode.notaryIdentity.owningKey)
|
||||||
logger.trace { "Received partially signed transaction: ${it.id}" }
|
logger.trace { "Received partially signed transaction: ${it.id}" }
|
||||||
|
|
||||||
// Download and check all the things that this transaction depends on and verify it is contract-valid,
|
// Download and check all the things that this transaction depends on and verify it is contract-valid,
|
||||||
// even though it is missing signatures.
|
// even though it is missing signatures.
|
||||||
subProtocol(ResolveTransactionsProtocol(wtx, otherParty))
|
subProtocol(ResolveTransactionsProtocol(wtx, otherParty))
|
||||||
|
|
||||||
if (wtx.outputs.map { it.data }.sumCashBy(myKeyPair.public).withoutIssuer() != price)
|
if (wtx.outputs.map { it.data }.sumCashBy(myPublicKey).withoutIssuer() != price)
|
||||||
throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
|
throw IllegalArgumentException("Transaction is not sending us the right amount of cash")
|
||||||
|
|
||||||
// There are all sorts of funny games a malicious secondary might play here, we should fix them:
|
// There are all sorts of funny games a malicious secondary might play here, we should fix them:
|
||||||
@ -227,17 +223,17 @@ object TwoPartyTradeProtocol {
|
|||||||
return sendAndReceive<SignaturesFromSeller>(otherParty, stx).unwrap { it }
|
return sendAndReceive<SignaturesFromSeller>(otherParty, stx).unwrap { it }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun signWithOurKeys(cashSigningPubKeys: List<PublicKey>, ptx: TransactionBuilder): SignedTransaction {
|
private fun signWithOurKeys(cashSigningPubKeys: List<PublicKeyTree>, ptx: TransactionBuilder): SignedTransaction {
|
||||||
// Now sign the transaction with whatever keys we need to move the cash.
|
// Now sign the transaction with whatever keys we need to move the cash.
|
||||||
for (k in cashSigningPubKeys) {
|
for (publicKey in cashSigningPubKeys.keys) {
|
||||||
val priv = serviceHub.keyManagementService.toPrivate(k)
|
val privateKey = serviceHub.keyManagementService.toPrivate(publicKey)
|
||||||
ptx.signWith(KeyPair(k, priv))
|
ptx.signWith(KeyPair(publicKey, privateKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptx.toSignedTransaction(checkSufficientSignatures = false)
|
return ptx.toSignedTransaction(checkSufficientSignatures = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKey>> {
|
private fun assembleSharedTX(tradeRequest: SellerTradeInfo): Pair<TransactionBuilder, List<PublicKeyTree>> {
|
||||||
val ptx = TransactionType.General.Builder(notary)
|
val ptx = TransactionType.General.Builder(notary)
|
||||||
|
|
||||||
// Add input and output states for the movement of cash, by using the Cash contract to generate the states
|
// Add input and output states for the movement of cash, by using the Cash contract to generate the states
|
||||||
@ -251,7 +247,7 @@ object TwoPartyTradeProtocol {
|
|||||||
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
|
// reveal who the owner actually is. The key management service is expected to derive a unique key from some
|
||||||
// initial seed in order to provide privacy protection.
|
// initial seed in order to provide privacy protection.
|
||||||
val freshKey = serviceHub.keyManagementService.freshKey()
|
val freshKey = serviceHub.keyManagementService.freshKey()
|
||||||
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public)
|
val (command, state) = tradeRequest.assetForSale.state.data.withNewOwner(freshKey.public.tree)
|
||||||
tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
|
tx.addOutputState(state, tradeRequest.assetForSale.state.notary)
|
||||||
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner)
|
tx.addCommand(command, tradeRequest.assetForSale.state.data.owner)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import net.corda.contracts.testing.fillWithSomeTestCash
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.days
|
import net.corda.core.days
|
||||||
import net.corda.core.node.recordTransactions
|
import net.corda.core.node.recordTransactions
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
@ -19,8 +20,8 @@ import net.corda.core.utilities.TEST_TX_TIME
|
|||||||
import net.corda.node.services.vault.NodeVaultService
|
import net.corda.node.services.vault.NodeVaultService
|
||||||
import net.corda.node.utilities.configureDatabase
|
import net.corda.node.utilities.configureDatabase
|
||||||
import net.corda.node.utilities.databaseTransaction
|
import net.corda.node.utilities.databaseTransaction
|
||||||
import net.corda.testing.node.MockServices
|
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.makeTestDataSourceProperties
|
import net.corda.testing.node.makeTestDataSourceProperties
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@ -277,8 +278,8 @@ class CommercialPaperTestsGeneric {
|
|||||||
// Alice pays $9000 to BigCorp to own some of their debt.
|
// Alice pays $9000 to BigCorp to own some of their debt.
|
||||||
moveTX = run {
|
moveTX = run {
|
||||||
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public)
|
aliceVaultService.generateSpend(ptx, 9000.DOLLARS, bigCorpServices.key.public.tree)
|
||||||
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public)
|
CommercialPaper().generateMove(ptx, issueTX.tx.outRef(0), aliceServices.key.public.tree)
|
||||||
ptx.signWith(bigCorpServices.key)
|
ptx.signWith(bigCorpServices.key)
|
||||||
ptx.signWith(aliceServices.key)
|
ptx.signWith(aliceServices.key)
|
||||||
ptx.signWith(DUMMY_NOTARY_KEY)
|
ptx.signWith(DUMMY_NOTARY_KEY)
|
||||||
|
@ -2,9 +2,7 @@ package net.corda.contracts.asset
|
|||||||
|
|
||||||
import net.corda.contracts.testing.fillWithSomeTestCash
|
import net.corda.contracts.testing.fillWithSomeTestCash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.serialization.OpaqueBytes
|
import net.corda.core.serialization.OpaqueBytes
|
||||||
@ -22,12 +20,10 @@ import net.corda.testing.node.MockKeyManagementService
|
|||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import net.corda.testing.node.makeTestDataSourceProperties
|
import net.corda.testing.node.makeTestDataSourceProperties
|
||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -459,7 +455,7 @@ class CashTests {
|
|||||||
// Spend tx generation
|
// Spend tx generation
|
||||||
|
|
||||||
val OUR_KEY: KeyPair by lazy { generateKeyPair() }
|
val OUR_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val OUR_PUBKEY_1: PublicKey get() = OUR_KEY.public
|
val OUR_PUBKEY_1: PublicKeyTree get() = OUR_KEY.public.tree
|
||||||
|
|
||||||
val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2
|
val THEIR_PUBKEY_1 = DUMMY_PUBKEY_2
|
||||||
|
|
||||||
@ -485,7 +481,7 @@ class CashTests {
|
|||||||
return tx.toWireTransaction()
|
return tx.toWireTransaction()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun makeSpend(amount: Amount<Currency>, dest: PublicKey): WireTransaction {
|
fun makeSpend(amount: Amount<Currency>, dest: PublicKeyTree): WireTransaction {
|
||||||
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
|
val tx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
databaseTransaction(database) {
|
databaseTransaction(database) {
|
||||||
vault.generateSpend(tx, amount, dest)
|
vault.generateSpend(tx, amount, dest)
|
||||||
|
@ -2,13 +2,13 @@ package net.corda.contracts.asset
|
|||||||
|
|
||||||
import net.corda.contracts.asset.Obligation.Lifecycle
|
import net.corda.contracts.asset.Obligation.Lifecycle
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.NullPublicKey
|
import net.corda.core.crypto.NullPublicKeyTree
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.serialization.OpaqueBytes
|
import net.corda.core.serialization.OpaqueBytes
|
||||||
import net.corda.core.utilities.*
|
import net.corda.core.utilities.*
|
||||||
import net.corda.testing.*
|
import net.corda.testing.*
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -506,7 +506,7 @@ class ObligationTests {
|
|||||||
val oneUnitFcoj = Amount(1, defaultFcoj)
|
val oneUnitFcoj = Amount(1, defaultFcoj)
|
||||||
val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME)
|
val obligationDef = Obligation.Terms(nonEmptySetOf(CommodityContract().legalContractReference), nonEmptySetOf(defaultFcoj), TEST_TX_TIME)
|
||||||
val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE,
|
val oneUnitFcojObligation = Obligation.State(Obligation.Lifecycle.NORMAL, ALICE,
|
||||||
obligationDef, oneUnitFcoj.quantity, NullPublicKey)
|
obligationDef, oneUnitFcoj.quantity, NullPublicKeyTree)
|
||||||
// Try settling a simple commodity obligation
|
// Try settling a simple commodity obligation
|
||||||
ledger {
|
ledger {
|
||||||
unverifiedTransaction {
|
unverifiedTransaction {
|
||||||
@ -830,7 +830,7 @@ class ObligationTests {
|
|||||||
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
|
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
|
||||||
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
|
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
|
||||||
)
|
)
|
||||||
val expected: Map<Pair<PublicKey, PublicKey>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
|
val expected: Map<Pair<PublicKeyTree, PublicKeyTree>, Amount<Currency>> = emptyMap() // Zero balances are stripped before returning
|
||||||
val actual = netAmountsDue(balanced)
|
val actual = netAmountsDue(balanced)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -851,8 +851,8 @@ class ObligationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `summing empty balances due between parties`() {
|
fun `summing empty balances due between parties`() {
|
||||||
val empty = emptyMap<Pair<PublicKey, PublicKey>, Amount<Currency>>()
|
val empty = emptyMap<Pair<PublicKeyTree, PublicKeyTree>, Amount<Currency>>()
|
||||||
val expected = emptyMap<PublicKey, Long>()
|
val expected = emptyMap<PublicKeyTree, Long>()
|
||||||
val actual = sumAmountsDue(empty)
|
val actual = sumAmountsDue(empty)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -872,7 +872,7 @@ class ObligationTests {
|
|||||||
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
|
Pair(Pair(ALICE_PUBKEY, BOB_PUBKEY), Amount(100000000, GBP)),
|
||||||
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
|
Pair(Pair(BOB_PUBKEY, ALICE_PUBKEY), Amount(100000000, GBP))
|
||||||
)
|
)
|
||||||
val expected: Map<PublicKey, Long> = emptyMap() // Zero balances are stripped before returning
|
val expected: Map<PublicKeyTree, Long> = emptyMap() // Zero balances are stripped before returning
|
||||||
val actual = sumAmountsDue(balanced)
|
val actual = sumAmountsDue(balanced)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.keys
|
||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.toStringShort
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
@ -101,7 +102,7 @@ class CordaRPCOpsImpl(
|
|||||||
val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder,
|
val (spendTX, keysForSigning) = services.vaultService.generateSpend(builder,
|
||||||
req.amount.withoutIssuer(), req.recipient.owningKey, setOf(req.amount.token.issuer.party))
|
req.amount.withoutIssuer(), req.recipient.owningKey, setOf(req.amount.token.issuer.party))
|
||||||
|
|
||||||
keysForSigning.forEach {
|
keysForSigning.keys.forEach {
|
||||||
val key = services.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}")
|
val key = services.keyManagementService.keys[it] ?: throw IllegalStateException("Could not find signing key for ${it.toStringShort()}")
|
||||||
builder.signWith(KeyPair(it, key))
|
builder.signWith(KeyPair(it, key))
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.node.services.identity
|
package net.corda.node.services.identity
|
||||||
|
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.node.services.IdentityService
|
import net.corda.core.node.services.IdentityService
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService {
|
class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService {
|
||||||
private val keyToParties = ConcurrentHashMap<PublicKey, Party>()
|
private val keyToParties = ConcurrentHashMap<PublicKeyTree, Party>()
|
||||||
private val nameToParties = ConcurrentHashMap<String, Party>()
|
private val nameToParties = ConcurrentHashMap<String, Party>()
|
||||||
|
|
||||||
override fun registerIdentity(party: Party) {
|
override fun registerIdentity(party: Party) {
|
||||||
@ -20,6 +20,6 @@ class InMemoryIdentityService() : SingletonSerializeAsToken(), IdentityService {
|
|||||||
nameToParties[party.name] = party
|
nameToParties[party.name] = party
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]
|
override fun partyFromKey(key: PublicKeyTree): Party? = keyToParties[key]
|
||||||
override fun partyFromName(name: String): Party? = nameToParties[name]
|
override fun partyFromName(name: String): Party? = nameToParties[name]
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,11 @@ package net.corda.node.services.messaging
|
|||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting
|
import com.google.common.annotations.VisibleForTesting
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import net.corda.core.crypto.parsePublicKeyBase58
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.toBase58String
|
|
||||||
import net.corda.core.messaging.MessageRecipients
|
import net.corda.core.messaging.MessageRecipients
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
|
||||||
import net.corda.core.read
|
import net.corda.core.read
|
||||||
|
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||||
import net.corda.node.services.config.NodeSSLConfiguration
|
import net.corda.node.services.config.NodeSSLConfiguration
|
||||||
import net.corda.node.services.config.configureWithDevSSLCertificate
|
import net.corda.node.services.config.configureWithDevSSLCertificate
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString
|
import org.apache.activemq.artemis.api.core.SimpleString
|
||||||
@ -18,7 +17,6 @@ import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants
|
|||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.PublicKey
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base class for Artemis services that defines shared data structures and transport configuration
|
* The base class for Artemis services that defines shared data structures and transport configuration
|
||||||
@ -77,7 +75,7 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
|
|||||||
* may change or evolve and code that relies upon it being a simple host/port may not function correctly.
|
* may change or evolve and code that relies upon it being a simple host/port may not function correctly.
|
||||||
* For instance it may contain onion routing data.
|
* For instance it may contain onion routing data.
|
||||||
*/
|
*/
|
||||||
data class NodeAddress(val identity: PublicKey, override val hostAndPort: HostAndPort) : SingleMessageRecipient, ArtemisAddress {
|
data class NodeAddress(val identity: PublicKeyTree, override val hostAndPort: HostAndPort) : SingleMessageRecipient, ArtemisAddress {
|
||||||
override val queueName: SimpleString by lazy { SimpleString(PEERS_PREFIX+identity.toBase58String()) }
|
override val queueName: SimpleString by lazy { SimpleString(PEERS_PREFIX+identity.toBase58String()) }
|
||||||
override fun toString(): String = "${javaClass.simpleName}(identity = $queueName, $hostAndPort)"
|
override fun toString(): String = "${javaClass.simpleName}(identity = $queueName, $hostAndPort)"
|
||||||
}
|
}
|
||||||
@ -85,9 +83,9 @@ abstract class ArtemisMessagingComponent() : SingletonSerializeAsToken() {
|
|||||||
/** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */
|
/** The config object is used to pass in the passwords for the certificate KeyStore and TrustStore */
|
||||||
abstract val config: NodeSSLConfiguration
|
abstract val config: NodeSSLConfiguration
|
||||||
|
|
||||||
protected fun parseKeyFromQueueName(name: String): PublicKey {
|
protected fun parseKeyFromQueueName(name: String): PublicKeyTree {
|
||||||
require(name.startsWith(PEERS_PREFIX))
|
require(name.startsWith(PEERS_PREFIX))
|
||||||
return parsePublicKeyBase58(name.substring(PEERS_PREFIX.length))
|
return PublicKeyTree.parseFromBase58(name.substring(PEERS_PREFIX.length))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected enum class ConnectionDirection { INBOUND, OUTBOUND }
|
protected enum class ConnectionDirection { INBOUND, OUTBOUND }
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.node.services.messaging
|
|||||||
|
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import net.corda.core.ThreadBox
|
import net.corda.core.ThreadBox
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.messaging.*
|
import net.corda.core.messaging.*
|
||||||
import net.corda.core.serialization.SerializedBytes
|
import net.corda.core.serialization.SerializedBytes
|
||||||
import net.corda.core.serialization.opaque
|
import net.corda.core.serialization.opaque
|
||||||
@ -19,7 +20,6 @@ import org.apache.activemq.artemis.api.core.client.*
|
|||||||
import org.jetbrains.exposed.sql.Database
|
import org.jetbrains.exposed.sql.Database
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import org.jetbrains.exposed.sql.statements.InsertStatement
|
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
@ -49,7 +49,7 @@ import javax.annotation.concurrent.ThreadSafe
|
|||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class NodeMessagingClient(override val config: NodeConfiguration,
|
class NodeMessagingClient(override val config: NodeConfiguration,
|
||||||
val serverHostPort: HostAndPort,
|
val serverHostPort: HostAndPort,
|
||||||
val myIdentity: PublicKey?,
|
val myIdentity: PublicKeyTree?,
|
||||||
val executor: AffinityExecutor,
|
val executor: AffinityExecutor,
|
||||||
val database: Database) : ArtemisMessagingComponent(), MessagingServiceInternal {
|
val database: Database) : ArtemisMessagingComponent(), MessagingServiceInternal {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -9,10 +9,15 @@ import com.esotericsoftware.kryo.io.Input
|
|||||||
import com.esotericsoftware.kryo.io.Output
|
import com.esotericsoftware.kryo.io.Output
|
||||||
import com.esotericsoftware.kryo.serializers.JavaSerializer
|
import com.esotericsoftware.kryo.serializers.JavaSerializer
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
|
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
||||||
|
import de.javakaffee.kryoserializers.guava.*
|
||||||
import net.corda.contracts.asset.Cash
|
import net.corda.contracts.asset.Cash
|
||||||
import net.corda.core.ErrorOr
|
import net.corda.core.ErrorOr
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
import net.corda.core.node.PhysicalLocation
|
import net.corda.core.node.PhysicalLocation
|
||||||
import net.corda.core.node.ServiceEntry
|
import net.corda.core.node.ServiceEntry
|
||||||
@ -23,9 +28,6 @@ import net.corda.core.serialization.*
|
|||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.node.services.User
|
import net.corda.node.services.User
|
||||||
import de.javakaffee.kryoserializers.ArraysAsListSerializer
|
|
||||||
import de.javakaffee.kryoserializers.guava.*
|
|
||||||
import net.corda.core.crypto.*
|
|
||||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
import net.i2p.crypto.eddsa.EdDSAPrivateKey
|
||||||
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
import net.i2p.crypto.eddsa.EdDSAPublicKey
|
||||||
import org.objenesis.strategy.StdInstantiatorStrategy
|
import org.objenesis.strategy.StdInstantiatorStrategy
|
||||||
@ -148,6 +150,8 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
|
|||||||
register(ByteArray::class.java)
|
register(ByteArray::class.java)
|
||||||
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
|
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
|
||||||
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
|
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
|
||||||
|
register(PublicKeyTree.Leaf::class.java)
|
||||||
|
register(PublicKeyTree.Node::class.java)
|
||||||
register(Vault::class.java)
|
register(Vault::class.java)
|
||||||
register(Vault.Update::class.java)
|
register(Vault.Update::class.java)
|
||||||
register(StateMachineRunId::class.java)
|
register(StateMachineRunId::class.java)
|
||||||
@ -179,7 +183,7 @@ private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null)
|
|||||||
register(ArtemisMessagingComponent.NodeAddress::class.java,
|
register(ArtemisMessagingComponent.NodeAddress::class.java,
|
||||||
read = { kryo, input ->
|
read = { kryo, input ->
|
||||||
ArtemisMessagingComponent.NodeAddress(
|
ArtemisMessagingComponent.NodeAddress(
|
||||||
parsePublicKeyBase58(kryo.readObject(input, String::class.java)),
|
PublicKeyTree.parseFromBase58(kryo.readObject(input, String::class.java)),
|
||||||
kryo.readObject(input, HostAndPort::class.java))
|
kryo.readObject(input, HostAndPort::class.java))
|
||||||
},
|
},
|
||||||
write = { kryo, output, nodeAddress ->
|
write = { kryo, output, nodeAddress ->
|
||||||
|
@ -6,7 +6,7 @@ import com.google.common.util.concurrent.SettableFuture
|
|||||||
import net.corda.core.bufferUntilSubscribed
|
import net.corda.core.bufferUntilSubscribed
|
||||||
import net.corda.core.contracts.Contract
|
import net.corda.core.contracts.Contract
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
import net.corda.core.crypto.toStringShort
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.map
|
import net.corda.core.map
|
||||||
import net.corda.core.messaging.MessagingService
|
import net.corda.core.messaging.MessagingService
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
@ -29,7 +29,6 @@ import net.corda.node.utilities.AddOrRemove
|
|||||||
import net.corda.protocols.sendRequest
|
import net.corda.protocols.sendRequest
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import java.security.PublicKey
|
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.annotation.concurrent.ThreadSafe
|
import javax.annotation.concurrent.ThreadSafe
|
||||||
@ -66,14 +65,14 @@ open class InMemoryNetworkMapCache : SingletonSerializeAsToken(), NetworkMapCach
|
|||||||
override fun get(serviceType: ServiceType) = registeredNodes.filterValues { it.advertisedServices.any { it.info.type.isSubTypeOf(serviceType) } }.map { it.value }
|
override fun get(serviceType: ServiceType) = registeredNodes.filterValues { it.advertisedServices.any { it.info.type.isSubTypeOf(serviceType) } }.map { it.value }
|
||||||
override fun getRecommended(type: ServiceType, contract: Contract, vararg party: Party): NodeInfo? = get(type).firstOrNull()
|
override fun getRecommended(type: ServiceType, contract: Contract, vararg party: Party): NodeInfo? = get(type).firstOrNull()
|
||||||
override fun getNodeByLegalName(name: String) = get().singleOrNull { it.legalIdentity.name == name }
|
override fun getNodeByLegalName(name: String) = get().singleOrNull { it.legalIdentity.name == name }
|
||||||
override fun getNodeByPublicKey(publicKey: PublicKey): NodeInfo? {
|
override fun getNodeByPublicKey(publicKey: PublicKeyTree): NodeInfo? {
|
||||||
// Although we should never have more than one match, it is theoretically possible. Report an error if it happens.
|
// Although we should never have more than one match, it is theoretically possible. Report an error if it happens.
|
||||||
val candidates = get().filter {
|
val candidates = get().filter {
|
||||||
(it.legalIdentity.owningKey == publicKey)
|
(it.legalIdentity.owningKey == publicKey)
|
||||||
|| it.advertisedServices.any { it.identity.owningKey == publicKey }
|
|| it.advertisedServices.any { it.identity.owningKey == publicKey }
|
||||||
}
|
}
|
||||||
if (candidates.size > 1) {
|
if (candidates.size > 1) {
|
||||||
throw IllegalStateException("Found more than one match for key ${publicKey.toStringShort()}")
|
throw IllegalStateException("Found more than one match for key $publicKey")
|
||||||
}
|
}
|
||||||
return candidates.singleOrNull()
|
return candidates.singleOrNull()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.corda.node.services.network
|
package net.corda.node.services.network
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting
|
import com.google.common.annotations.VisibleForTesting
|
||||||
|
import kotlinx.support.jdk8.collections.compute
|
||||||
import net.corda.core.ThreadBox
|
import net.corda.core.ThreadBox
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
@ -23,7 +24,6 @@ import net.corda.node.services.api.AbstractNodeService
|
|||||||
import net.corda.node.services.api.ServiceHubInternal
|
import net.corda.node.services.api.ServiceHubInternal
|
||||||
import net.corda.node.utilities.AddOrRemove
|
import net.corda.node.utilities.AddOrRemove
|
||||||
import net.corda.protocols.ServiceRequestMessage
|
import net.corda.protocols.ServiceRequestMessage
|
||||||
import kotlinx.support.jdk8.collections.compute
|
|
||||||
import java.security.PrivateKey
|
import java.security.PrivateKey
|
||||||
import java.security.SignatureException
|
import java.security.SignatureException
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -333,7 +333,7 @@ class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemo
|
|||||||
*/
|
*/
|
||||||
fun toWire(privateKey: PrivateKey): WireNodeRegistration {
|
fun toWire(privateKey: PrivateKey): WireNodeRegistration {
|
||||||
val regSerialized = this.serialize()
|
val regSerialized = this.serialize()
|
||||||
val regSig = privateKey.signWithECDSA(regSerialized.bits, node.legalIdentity.owningKey)
|
val regSig = privateKey.signWithECDSA(regSerialized.bits, node.legalIdentity.owningKey.singleKey)
|
||||||
|
|
||||||
return WireNodeRegistration(regSerialized, regSig)
|
return WireNodeRegistration(regSerialized, regSig)
|
||||||
}
|
}
|
||||||
@ -347,7 +347,7 @@ class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemo
|
|||||||
class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalSignature.WithKey) : SignedData<NodeRegistration>(raw, sig) {
|
class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalSignature.WithKey) : SignedData<NodeRegistration>(raw, sig) {
|
||||||
@Throws(IllegalArgumentException::class)
|
@Throws(IllegalArgumentException::class)
|
||||||
override fun verifyData(data: NodeRegistration) {
|
override fun verifyData(data: NodeRegistration) {
|
||||||
require(data.node.legalIdentity.owningKey == sig.by)
|
require(data.node.legalIdentity.owningKey.isFulfilledBy(sig.by))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.ThreadBox
|
|||||||
import net.corda.core.bufferUntilSubscribed
|
import net.corda.core.bufferUntilSubscribed
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.services.Vault
|
import net.corda.core.node.services.Vault
|
||||||
@ -155,8 +156,8 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
|
|||||||
*/
|
*/
|
||||||
override fun generateSpend(tx: TransactionBuilder,
|
override fun generateSpend(tx: TransactionBuilder,
|
||||||
amount: Amount<Currency>,
|
amount: Amount<Currency>,
|
||||||
to: PublicKey,
|
to: PublicKeyTree,
|
||||||
onlyFromParties: Set<Party>?): Pair<TransactionBuilder, List<PublicKey>> {
|
onlyFromParties: Set<Party>?): Pair<TransactionBuilder, List<PublicKeyTree>> {
|
||||||
// Discussion
|
// Discussion
|
||||||
//
|
//
|
||||||
// This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline.
|
// This code is analogous to the Wallet.send() set of methods in bitcoinj, and has the same general outline.
|
||||||
@ -239,7 +240,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
|
|||||||
return Pair(tx, keysList)
|
return Pair(tx, keysList)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKey)
|
private fun deriveState(txState: TransactionState<Cash.State>, amount: Amount<Issued<Currency>>, owner: PublicKeyTree)
|
||||||
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
= txState.copy(data = txState.data.copy(amount = amount, owner = owner))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,7 +293,7 @@ class NodeVaultService(private val services: ServiceHub) : SingletonSerializeAsT
|
|||||||
|
|
||||||
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>): Boolean {
|
private fun isRelevant(state: ContractState, ourKeys: Set<PublicKey>): Boolean {
|
||||||
return if (state is OwnableState) {
|
return if (state is OwnableState) {
|
||||||
state.owner in ourKeys
|
state.owner.containsAny(ourKeys)
|
||||||
} else if (state is LinearState) {
|
} else if (state is LinearState) {
|
||||||
// It's potentially of interest to the vault
|
// It's potentially of interest to the vault
|
||||||
state.isRelevant(ourKeys)
|
state.isRelevant(ourKeys)
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
package net.corda.node.utilities
|
package net.corda.node.utilities
|
||||||
|
|
||||||
import co.paralleluniverse.strands.Strand
|
import co.paralleluniverse.strands.Strand
|
||||||
|
import com.zaxxer.hikari.HikariConfig
|
||||||
|
import com.zaxxer.hikari.HikariDataSource
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.crypto.parsePublicKeyBase58
|
import net.corda.core.crypto.parsePublicKeyBase58
|
||||||
import net.corda.core.crypto.toBase58String
|
import net.corda.core.crypto.toBase58String
|
||||||
import com.zaxxer.hikari.HikariConfig
|
|
||||||
import com.zaxxer.hikari.HikariDataSource
|
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.transactions.TransactionInterface
|
import org.jetbrains.exposed.sql.transactions.TransactionInterface
|
||||||
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
import org.jetbrains.exposed.sql.transactions.TransactionManager
|
||||||
@ -137,15 +138,17 @@ class StrandLocalTransactionManager(initWithDatabase: Database) : TransactionMan
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Composite columns for use with below Exposed helpers.
|
// Composite columns for use with below Exposed helpers.
|
||||||
data class PartyColumns(val name: Column<String>, val owningKey: Column<PublicKey>)
|
data class PartyColumns(val name: Column<String>, val owningKey: Column<PublicKeyTree>)
|
||||||
data class StateRefColumns(val txId: Column<SecureHash>, val index: Column<Int>)
|
data class StateRefColumns(val txId: Column<SecureHash>, val index: Column<Int>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [Table] column helpers for use with Exposed, as per [varchar] etc.
|
* [Table] column helpers for use with Exposed, as per [varchar] etc.
|
||||||
*/
|
*/
|
||||||
fun Table.publicKey(name: String) = this.registerColumn<PublicKey>(name, PublicKeyColumnType)
|
fun Table.publicKey(name: String) = this.registerColumn<PublicKey>(name, PublicKeyColumnType)
|
||||||
|
|
||||||
|
fun Table.publicKeyTree(name: String) = this.registerColumn<PublicKeyTree>(name, PublicKeyTreeColumnType)
|
||||||
fun Table.secureHash(name: String) = this.registerColumn<SecureHash>(name, SecureHashColumnType)
|
fun Table.secureHash(name: String) = this.registerColumn<SecureHash>(name, SecureHashColumnType)
|
||||||
fun Table.party(nameColumnName: String, keyColumnName: String) = PartyColumns(this.varchar(nameColumnName, length = 255), this.publicKey(keyColumnName))
|
fun Table.party(nameColumnName: String, keyColumnName: String) = PartyColumns(this.varchar(nameColumnName, length = 255), this.publicKeyTree(keyColumnName))
|
||||||
fun Table.uuidString(name: String) = this.registerColumn<UUID>(name, UUIDStringColumnType)
|
fun Table.uuidString(name: String) = this.registerColumn<UUID>(name, UUIDStringColumnType)
|
||||||
fun Table.localDate(name: String) = this.registerColumn<LocalDate>(name, LocalDateColumnType)
|
fun Table.localDate(name: String) = this.registerColumn<LocalDate>(name, LocalDateColumnType)
|
||||||
fun Table.localDateTime(name: String) = this.registerColumn<LocalDateTime>(name, LocalDateTimeColumnType)
|
fun Table.localDateTime(name: String) = this.registerColumn<LocalDateTime>(name, LocalDateTimeColumnType)
|
||||||
@ -163,6 +166,15 @@ object PublicKeyColumnType : ColumnType() {
|
|||||||
override fun notNullValueToDB(value: Any): Any = if (value is PublicKey) value.toBase58String() else value
|
override fun notNullValueToDB(value: Any): Any = if (value is PublicKey) value.toBase58String() else value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [ColumnType] for marshalling to/from database on behalf of [PublicKeyTree].
|
||||||
|
*/
|
||||||
|
object PublicKeyTreeColumnType : ColumnType() {
|
||||||
|
override fun sqlType(): String = "VARCHAR"
|
||||||
|
override fun valueFromDB(value: Any): Any = PublicKeyTree.parseFromBase58(value.toString())
|
||||||
|
override fun notNullValueToDB(value: Any): Any = if (value is PublicKeyTree) value.toBase58String() else value
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [ColumnType] for marshalling to/from database on behalf of [SecureHash].
|
* [ColumnType] for marshalling to/from database on behalf of [SecureHash].
|
||||||
*/
|
*/
|
||||||
|
@ -146,8 +146,9 @@ class CordaRPCOpsImplTest {
|
|||||||
require(tx.tx.outputs.size == 1)
|
require(tx.tx.outputs.size == 1)
|
||||||
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
||||||
// Only Alice signed
|
// Only Alice signed
|
||||||
require(signaturePubKeys.size == 1)
|
val aliceKey = aliceNode.info.legalIdentity.owningKey
|
||||||
require(signaturePubKeys.contains(aliceNode.info.legalIdentity.owningKey))
|
require(signaturePubKeys.size <= aliceKey.keys.size)
|
||||||
|
require(aliceKey.isFulfilledBy(signaturePubKeys))
|
||||||
},
|
},
|
||||||
// MOVE
|
// MOVE
|
||||||
expect { tx ->
|
expect { tx ->
|
||||||
@ -155,9 +156,8 @@ class CordaRPCOpsImplTest {
|
|||||||
require(tx.tx.outputs.size == 1)
|
require(tx.tx.outputs.size == 1)
|
||||||
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
val signaturePubKeys = tx.sigs.map { it.by }.toSet()
|
||||||
// Alice and Notary signed
|
// Alice and Notary signed
|
||||||
require(signaturePubKeys.size == 2)
|
require(aliceNode.info.legalIdentity.owningKey.isFulfilledBy(signaturePubKeys))
|
||||||
require(signaturePubKeys.contains(aliceNode.info.legalIdentity.owningKey))
|
require(notaryNode.info.notaryIdentity.owningKey.isFulfilledBy(signaturePubKeys))
|
||||||
require(signaturePubKeys.contains(notaryNode.info.notaryIdentity.owningKey))
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ import net.corda.contracts.asset.*
|
|||||||
import net.corda.contracts.testing.fillWithSomeTestCash
|
import net.corda.contracts.testing.fillWithSomeTestCash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.days
|
import net.corda.core.days
|
||||||
import net.corda.core.map
|
import net.corda.core.map
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
@ -40,7 +42,6 @@ import rx.Observable
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
import java.util.concurrent.Future
|
import java.util.concurrent.Future
|
||||||
@ -250,7 +251,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
}
|
}
|
||||||
val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray()))
|
val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray()))
|
||||||
|
|
||||||
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public, notaryNode.info.notaryIdentity).second
|
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public.tree, notaryNode.info.notaryIdentity).second
|
||||||
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode)
|
val bobsSignedTxns = insertFakeTransactions(bobsFakeCash, bobNode)
|
||||||
val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
||||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
||||||
@ -342,7 +343,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
}
|
}
|
||||||
val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray()))
|
val attachmentID = attachment(ByteArrayInputStream(stream.toByteArray()))
|
||||||
|
|
||||||
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public, notaryNode.info.notaryIdentity).second
|
val bobsFakeCash = fillUpForBuyer(false, bobNode.keyManagement.freshKey().public.tree, notaryNode.info.notaryIdentity).second
|
||||||
insertFakeTransactions(bobsFakeCash, bobNode)
|
insertFakeTransactions(bobsFakeCash, bobNode)
|
||||||
val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
val alicesFakePaper = fillUpForSeller(false, aliceNode.info.legalIdentity.owningKey,
|
||||||
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` DUMMY_CASH_ISSUER, attachmentID, notaryNode.info.notaryIdentity).second
|
||||||
@ -433,7 +434,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
val bobKey = bobNode.services.legalIdentityKey
|
val bobKey = bobNode.services.legalIdentityKey
|
||||||
val issuer = MEGA_CORP.ref(1, 2, 3)
|
val issuer = MEGA_CORP.ref(1, 2, 3)
|
||||||
|
|
||||||
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public, notaryNode.info.notaryIdentity).second
|
val bobsBadCash = fillUpForBuyer(bobError, bobKey.public.tree, notaryNode.info.notaryIdentity).second
|
||||||
val alicesFakePaper = fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey,
|
val alicesFakePaper = fillUpForSeller(aliceError, aliceNode.info.legalIdentity.owningKey,
|
||||||
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
|
1200.DOLLARS `issued by` issuer, null, notaryNode.info.notaryIdentity).second
|
||||||
|
|
||||||
@ -480,7 +481,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
|
|
||||||
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
|
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
|
||||||
withError: Boolean,
|
withError: Boolean,
|
||||||
owner: PublicKey = BOB_PUBKEY,
|
owner: PublicKeyTree = BOB_PUBKEY,
|
||||||
notary: Party): Pair<Vault, List<WireTransaction>> {
|
notary: Party): Pair<Vault, List<WireTransaction>> {
|
||||||
val issuer = DUMMY_CASH_ISSUER
|
val issuer = DUMMY_CASH_ISSUER
|
||||||
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
||||||
@ -490,10 +491,10 @@ class TwoPartyTradeProtocolTests {
|
|||||||
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
output("elbonian money 1", notary = notary) { 800.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||||
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
output("elbonian money 2", notary = notary) { 1000.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||||
if (!withError)
|
if (!withError)
|
||||||
command(DUMMY_CASH_ISSUER_KEY.public) { Cash.Commands.Issue() }
|
command(DUMMY_CASH_ISSUER_KEY.public.tree) { Cash.Commands.Issue() }
|
||||||
else
|
else
|
||||||
// Put a broken command on so at least a signature is created
|
// Put a broken command on so at least a signature is created
|
||||||
command(DUMMY_CASH_ISSUER_KEY.public) { Cash.Commands.Move() }
|
command(DUMMY_CASH_ISSUER_KEY.public.tree) { Cash.Commands.Move() }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
if (withError) {
|
if (withError) {
|
||||||
this.fails()
|
this.fails()
|
||||||
@ -524,7 +525,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
|
|
||||||
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
|
private fun LedgerDSL<TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
|
||||||
withError: Boolean,
|
withError: Boolean,
|
||||||
owner: PublicKey,
|
owner: PublicKeyTree,
|
||||||
amount: Amount<Issued<Currency>>,
|
amount: Amount<Issued<Currency>>,
|
||||||
attachmentID: SecureHash?,
|
attachmentID: SecureHash?,
|
||||||
notary: Party): Pair<Vault, List<WireTransaction>> {
|
notary: Party): Pair<Vault, List<WireTransaction>> {
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
package net.corda.node.services
|
package net.corda.node.services
|
||||||
|
|
||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import net.corda.core.contracts.ClientToServiceCommand
|
|
||||||
import net.corda.core.contracts.ContractState
|
|
||||||
import net.corda.core.contracts.StateAndRef
|
|
||||||
import net.corda.core.crypto.Party
|
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.messaging.Message
|
import net.corda.core.messaging.Message
|
||||||
import net.corda.core.messaging.createMessage
|
import net.corda.core.messaging.createMessage
|
||||||
import net.corda.core.node.services.DEFAULT_SESSION_ID
|
import net.corda.core.node.services.DEFAULT_SESSION_ID
|
||||||
@ -147,7 +143,7 @@ class ArtemisMessagingTests {
|
|||||||
|
|
||||||
private fun createMessagingClient(server: HostAndPort = hostAndPort): NodeMessagingClient {
|
private fun createMessagingClient(server: HostAndPort = hostAndPort): NodeMessagingClient {
|
||||||
return databaseTransaction(database) {
|
return databaseTransaction(database) {
|
||||||
NodeMessagingClient(config, server, identity.public, AffinityExecutor.ServiceAffinityExecutor("ArtemisMessagingTests", 1), database).apply {
|
NodeMessagingClient(config, server, identity.public.tree, AffinityExecutor.ServiceAffinityExecutor("ArtemisMessagingTests", 1), database).apply {
|
||||||
configureWithDevSSLCertificate()
|
configureWithDevSSLCertificate()
|
||||||
messagingClient = this
|
messagingClient = this
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package net.corda.node.services
|
package net.corda.node.services
|
||||||
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
import net.corda.core.crypto.generateKeyPair
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.node.services.network.NetworkMapService
|
import net.corda.node.services.network.NetworkMapService
|
||||||
import net.corda.node.services.transactions.SimpleNotaryService
|
|
||||||
import net.corda.testing.expect
|
import net.corda.testing.expect
|
||||||
import net.corda.testing.node.MockNetwork
|
import net.corda.testing.node.MockNetwork
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -34,12 +34,12 @@ class InMemoryNetworkMapCacheTest {
|
|||||||
val nodeB = network.createNode(null, -1, MockNetwork.DefaultFactory, true, "Node B", keyPair, ServiceInfo(NetworkMapService.type))
|
val nodeB = network.createNode(null, -1, MockNetwork.DefaultFactory, true, "Node B", keyPair, ServiceInfo(NetworkMapService.type))
|
||||||
|
|
||||||
// Node A currently knows only about itself, so this returns node A
|
// Node A currently knows only about itself, so this returns node A
|
||||||
assertEquals(nodeA.netMapCache.getNodeByPublicKey(keyPair.public), nodeA.info)
|
assertEquals(nodeA.netMapCache.getNodeByPublicKey(keyPair.public.tree), nodeA.info)
|
||||||
|
|
||||||
nodeA.netMapCache.addNode(nodeB.info)
|
nodeA.netMapCache.addNode(nodeB.info)
|
||||||
// Now both nodes match, so it throws an error
|
// Now both nodes match, so it throws an error
|
||||||
expect<IllegalStateException> {
|
expect<IllegalStateException> {
|
||||||
nodeA.netMapCache.getNodeByPublicKey(keyPair.public)
|
nodeA.netMapCache.getNodeByPublicKey(keyPair.public.tree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.corda.node.services
|
package net.corda.node.services
|
||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.days
|
import net.corda.core.days
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.node.recordTransactions
|
import net.corda.core.node.recordTransactions
|
||||||
@ -109,7 +111,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TestState(val protocolLogicRef: ProtocolLogicRef, val instant: Instant) : LinearState, SchedulableState {
|
class TestState(val protocolLogicRef: ProtocolLogicRef, val instant: Instant) : LinearState, SchedulableState {
|
||||||
override val participants: List<PublicKey>
|
override val participants: List<PublicKeyTree>
|
||||||
get() = throw UnsupportedOperationException()
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
override val linearId = UniqueIdentifier()
|
override val linearId = UniqueIdentifier()
|
||||||
@ -268,7 +270,7 @@ class NodeSchedulerServiceTest : SingletonSerializeAsToken() {
|
|||||||
val state = TestState(factory.create(TestProtocolLogic::class.java, increment), instant)
|
val state = TestState(factory.create(TestProtocolLogic::class.java, increment), instant)
|
||||||
val usefulTX = TransactionType.General.Builder(null).apply {
|
val usefulTX = TransactionType.General.Builder(null).apply {
|
||||||
addOutputState(state, DUMMY_NOTARY)
|
addOutputState(state, DUMMY_NOTARY)
|
||||||
addCommand(Command(), freshKey.public)
|
addCommand(Command(), freshKey.public.tree)
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
val txHash = usefulTX.id
|
val txHash = usefulTX.id
|
||||||
|
@ -6,6 +6,7 @@ import net.corda.core.contracts.DOLLARS
|
|||||||
import net.corda.core.contracts.POUNDS
|
import net.corda.core.contracts.POUNDS
|
||||||
import net.corda.core.contracts.TransactionType
|
import net.corda.core.contracts.TransactionType
|
||||||
import net.corda.core.contracts.`issued by`
|
import net.corda.core.contracts.`issued by`
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.node.services.TxWritableStorageService
|
import net.corda.core.node.services.TxWritableStorageService
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -102,7 +103,7 @@ class NodeVaultServiceTest {
|
|||||||
|
|
||||||
// Issue a txn to Send us some Money
|
// Issue a txn to Send us some Money
|
||||||
val usefulTX = TransactionType.General.Builder(null).apply {
|
val usefulTX = TransactionType.General.Builder(null).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public.tree, DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
|
||||||
@ -116,7 +117,7 @@ class NodeVaultServiceTest {
|
|||||||
|
|
||||||
// Issue more Money (GBP)
|
// Issue more Money (GBP)
|
||||||
val anotherTX = TransactionType.General.Builder(null).apply {
|
val anotherTX = TransactionType.General.Builder(null).apply {
|
||||||
Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 200.POUNDS `issued by` MEGA_CORP.ref(1), freshKey.public.tree, DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package net.corda.node.services
|
|||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.DigitalSignature
|
import net.corda.core.crypto.DigitalSignature
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.node.services.ServiceInfo
|
import net.corda.core.node.services.ServiceInfo
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
@ -56,7 +57,7 @@ class ValidatingNotaryServiceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test fun `should report error for missing signatures`() {
|
@Test fun `should report error for missing signatures`() {
|
||||||
val expectedMissingKey = MEGA_CORP_KEY.public
|
val expectedMissingKey = MEGA_CORP_KEY.public.tree
|
||||||
val stx = run {
|
val stx = run {
|
||||||
val inputState = issueState(clientNode)
|
val inputState = issueState(clientNode)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.contracts.asset.Cash
|
|||||||
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
import net.corda.contracts.asset.DUMMY_CASH_ISSUER
|
||||||
import net.corda.contracts.testing.fillWithSomeTestCash
|
import net.corda.contracts.testing.fillWithSomeTestCash
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
import net.corda.core.node.recordTransactions
|
import net.corda.core.node.recordTransactions
|
||||||
import net.corda.core.node.services.VaultService
|
import net.corda.core.node.services.VaultService
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
@ -72,7 +73,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
val state = w.states.toList()[0].state.data as Cash.State
|
val state = w.states.toList()[0].state.data as Cash.State
|
||||||
assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount)
|
assertEquals(30.45.DOLLARS `issued by` DUMMY_CASH_ISSUER, state.amount)
|
||||||
assertEquals(services.key.public, state.owner)
|
assertEquals(services.key.public.tree, state.owner)
|
||||||
|
|
||||||
assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[2].state.data as Cash.State).amount)
|
assertEquals(34.70.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[2].state.data as Cash.State).amount)
|
||||||
assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[1].state.data as Cash.State).amount)
|
assertEquals(34.85.DOLLARS `issued by` DUMMY_CASH_ISSUER, (w.states.toList()[1].state.data as Cash.State).amount)
|
||||||
@ -85,7 +86,7 @@ class VaultWithCashTest {
|
|||||||
// A tx that sends us money.
|
// A tx that sends us money.
|
||||||
val freshKey = services.keyManagementService.freshKey()
|
val freshKey = services.keyManagementService.freshKey()
|
||||||
val usefulTX = TransactionType.General.Builder(null).apply {
|
val usefulTX = TransactionType.General.Builder(null).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), freshKey.public.tree, DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// A tx that doesn't send us anything.
|
// A tx that doesn't send us anything.
|
||||||
val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
val irrelevantTX = TransactionType.General.Builder(DUMMY_NOTARY).apply {
|
||||||
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_KEY.public, DUMMY_NOTARY)
|
Cash().generateIssue(this, 100.DOLLARS `issued by` MEGA_CORP.ref(1), BOB_KEY.public.tree, DUMMY_NOTARY)
|
||||||
signWith(MEGA_CORP_KEY)
|
signWith(MEGA_CORP_KEY)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -127,8 +128,8 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// Issue a linear state
|
// Issue a linear state
|
||||||
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -148,7 +149,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// Issue a linear state
|
// Issue a linear state
|
||||||
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyIssue = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
|
||||||
signWith(freshKey)
|
signWith(freshKey)
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
@ -160,7 +161,7 @@ class VaultWithCashTest {
|
|||||||
|
|
||||||
// Move the same state
|
// Move the same state
|
||||||
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
val dummyMove = TransactionType.General.Builder(notary = DUMMY_NOTARY).apply {
|
||||||
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public)))
|
addOutputState(DummyLinearContract.State(linearId = linearId, participants = listOf(freshKey.public.tree)))
|
||||||
addInputState(dummyIssue.tx.outRef<LinearState>(0))
|
addInputState(dummyIssue.tx.outRef<LinearState>(0))
|
||||||
signWith(DUMMY_NOTARY_KEY)
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
}.toSignedTransaction()
|
}.toSignedTransaction()
|
||||||
|
@ -6,10 +6,9 @@ import com.google.common.base.Throwables
|
|||||||
import com.google.common.net.HostAndPort
|
import com.google.common.net.HostAndPort
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import com.google.common.util.concurrent.SettableFuture
|
import com.google.common.util.concurrent.SettableFuture
|
||||||
|
import com.typesafe.config.Config
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.protocols.ProtocolLogic
|
import net.corda.core.protocols.ProtocolLogic
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
@ -21,11 +20,9 @@ import net.corda.node.services.statemachine.StateMachineManager.Change
|
|||||||
import net.corda.node.utilities.AddOrRemove.ADD
|
import net.corda.node.utilities.AddOrRemove.ADD
|
||||||
import net.corda.testing.node.MockIdentityService
|
import net.corda.testing.node.MockIdentityService
|
||||||
import net.corda.testing.node.MockServices
|
import net.corda.testing.node.MockServices
|
||||||
import com.typesafe.config.Config
|
|
||||||
import rx.Subscriber
|
import rx.Subscriber
|
||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,24 +45,24 @@ import kotlin.reflect.KClass
|
|||||||
|
|
||||||
// A few dummy values for testing.
|
// A few dummy values for testing.
|
||||||
val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() }
|
val MEGA_CORP_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val MEGA_CORP_PUBKEY: PublicKey get() = MEGA_CORP_KEY.public
|
val MEGA_CORP_PUBKEY: PublicKeyTree get() = MEGA_CORP_KEY.public.tree
|
||||||
|
|
||||||
val MINI_CORP_KEY: KeyPair by lazy { generateKeyPair() }
|
val MINI_CORP_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val MINI_CORP_PUBKEY: PublicKey get() = MINI_CORP_KEY.public
|
val MINI_CORP_PUBKEY: PublicKeyTree get() = MINI_CORP_KEY.public.tree
|
||||||
|
|
||||||
val ORACLE_KEY: KeyPair by lazy { generateKeyPair() }
|
val ORACLE_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val ORACLE_PUBKEY: PublicKey get() = ORACLE_KEY.public
|
val ORACLE_PUBKEY: PublicKeyTree get() = ORACLE_KEY.public.tree
|
||||||
|
|
||||||
val ALICE_KEY: KeyPair by lazy { generateKeyPair() }
|
val ALICE_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val ALICE_PUBKEY: PublicKey get() = ALICE_KEY.public
|
val ALICE_PUBKEY: PublicKeyTree get() = ALICE_KEY.public.tree
|
||||||
val ALICE: Party get() = Party("Alice", ALICE_PUBKEY)
|
val ALICE: Party get() = Party("Alice", ALICE_PUBKEY)
|
||||||
|
|
||||||
val BOB_KEY: KeyPair by lazy { generateKeyPair() }
|
val BOB_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val BOB_PUBKEY: PublicKey get() = BOB_KEY.public
|
val BOB_PUBKEY: PublicKeyTree get() = BOB_KEY.public.tree
|
||||||
val BOB: Party get() = Party("Bob", BOB_PUBKEY)
|
val BOB: Party get() = Party("Bob", BOB_PUBKEY)
|
||||||
|
|
||||||
val CHARLIE_KEY: KeyPair by lazy { generateKeyPair() }
|
val CHARLIE_KEY: KeyPair by lazy { generateKeyPair() }
|
||||||
val CHARLIE_PUBKEY: PublicKey get() = CHARLIE_KEY.public
|
val CHARLIE_PUBKEY: PublicKeyTree get() = CHARLIE_KEY.public.tree
|
||||||
val CHARLIE: Party get() = Party("Charlie", CHARLIE_PUBKEY)
|
val CHARLIE: Party get() = Party("Charlie", CHARLIE_PUBKEY)
|
||||||
|
|
||||||
val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY)
|
val MEGA_CORP: Party get() = Party("MegaCorp", MEGA_CORP_PUBKEY)
|
||||||
|
@ -4,6 +4,7 @@ import net.corda.core.contracts.*
|
|||||||
import net.corda.core.contracts.clauses.Clause
|
import net.corda.core.contracts.clauses.Clause
|
||||||
import net.corda.core.contracts.clauses.FilterOn
|
import net.corda.core.contracts.clauses.FilterOn
|
||||||
import net.corda.core.contracts.clauses.verifyClause
|
import net.corda.core.contracts.clauses.verifyClause
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
|
||||||
@ -18,11 +19,11 @@ class DummyLinearContract: Contract {
|
|||||||
class State(
|
class State(
|
||||||
override val linearId: UniqueIdentifier = UniqueIdentifier(),
|
override val linearId: UniqueIdentifier = UniqueIdentifier(),
|
||||||
override val contract: Contract = DummyLinearContract(),
|
override val contract: Contract = DummyLinearContract(),
|
||||||
override val participants: List<PublicKey> = listOf(),
|
override val participants: List<PublicKeyTree> = listOf(),
|
||||||
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
|
val nonce: SecureHash = SecureHash.randomSHA256()) : LinearState {
|
||||||
|
|
||||||
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
override fun isRelevant(ourKeys: Set<PublicKey>): Boolean {
|
||||||
return participants.any { ourKeys.contains(it) }
|
return participants.any { it.containsAny(ourKeys) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import net.corda.core.transactions.SignedTransaction
|
|||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.transactions.WireTransaction
|
import net.corda.core.transactions.WireTransaction
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
import net.corda.core.utilities.DUMMY_NOTARY_KEY
|
||||||
import net.corda.core.crypto.*
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -130,7 +129,7 @@ data class TestTransactionDSLInterpreter private constructor(
|
|||||||
transactionBuilder.addAttachment(attachmentId)
|
transactionBuilder.addAttachment(attachmentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun _command(signers: List<PublicKey>, commandData: CommandData) {
|
override fun _command(signers: List<PublicKeyTree>, commandData: CommandData) {
|
||||||
val command = Command(commandData, signers)
|
val command = Command(commandData, signers)
|
||||||
transactionBuilder.addCommand(command)
|
transactionBuilder.addCommand(command)
|
||||||
}
|
}
|
||||||
@ -325,7 +324,7 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: List<KeyPair>)
|
|||||||
(ALL_TEST_KEYS + extraKeys).forEach {
|
(ALL_TEST_KEYS + extraKeys).forEach {
|
||||||
keyLookup[it.public] = it
|
keyLookup[it.public] = it
|
||||||
}
|
}
|
||||||
wtx.mustSign.forEach {
|
wtx.mustSign.keys.forEach {
|
||||||
val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}")
|
val key = keyLookup[it] ?: throw IllegalArgumentException("Missing required key for ${it.toStringShort()}")
|
||||||
signatures += key.signWithECDSA(wtx.id)
|
signatures += key.signWithECDSA(wtx.id)
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,11 @@ package net.corda.testing
|
|||||||
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.Party
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.seconds
|
import net.corda.core.seconds
|
||||||
import net.corda.core.transactions.TransactionBuilder
|
import net.corda.core.transactions.TransactionBuilder
|
||||||
import net.corda.core.utilities.DUMMY_NOTARY
|
import net.corda.core.utilities.DUMMY_NOTARY
|
||||||
import java.security.PublicKey
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ interface TransactionDSLInterpreter : Verifies, OutputStateLookup {
|
|||||||
* @param signers The signer public keys.
|
* @param signers The signer public keys.
|
||||||
* @param commandData The contents of the command.
|
* @param commandData The contents of the command.
|
||||||
*/
|
*/
|
||||||
fun _command(signers: List<PublicKey>, commandData: CommandData)
|
fun _command(signers: List<PublicKeyTree>, commandData: CommandData)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a timestamp to the transaction.
|
* Adds a timestamp to the transaction.
|
||||||
@ -99,12 +99,12 @@ class TransactionDSL<out T : TransactionDSLInterpreter>(val interpreter: T) : Tr
|
|||||||
/**
|
/**
|
||||||
* @see TransactionDSLInterpreter._command
|
* @see TransactionDSLInterpreter._command
|
||||||
*/
|
*/
|
||||||
fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) =
|
fun command(vararg signers: PublicKeyTree, commandDataClosure: () -> CommandData) =
|
||||||
_command(listOf(*signers), commandDataClosure())
|
_command(listOf(*signers), commandDataClosure())
|
||||||
/**
|
/**
|
||||||
* @see TransactionDSLInterpreter._command
|
* @see TransactionDSLInterpreter._command
|
||||||
*/
|
*/
|
||||||
fun command(signer: PublicKey, commandData: CommandData) = _command(listOf(signer), commandData)
|
fun command(signer: PublicKeyTree, commandData: CommandData) = _command(listOf(signer), commandData)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a timestamp command to the transaction.
|
* Adds a timestamp command to the transaction.
|
||||||
|
@ -2,10 +2,7 @@ package net.corda.testing.node
|
|||||||
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import net.corda.core.contracts.Attachment
|
import net.corda.core.contracts.Attachment
|
||||||
import net.corda.core.crypto.Party
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.generateKeyPair
|
|
||||||
import net.corda.core.crypto.sha256
|
|
||||||
import net.corda.core.messaging.MessagingService
|
import net.corda.core.messaging.MessagingService
|
||||||
import net.corda.core.messaging.SingleMessageRecipient
|
import net.corda.core.messaging.SingleMessageRecipient
|
||||||
import net.corda.core.node.NodeInfo
|
import net.corda.core.node.NodeInfo
|
||||||
@ -63,18 +60,18 @@ open class MockServices(val key: KeyPair = generateKeyPair()) : ServiceHub {
|
|||||||
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
|
override val networkMapCache: NetworkMapCache get() = throw UnsupportedOperationException()
|
||||||
override val clock: Clock get() = throw UnsupportedOperationException()
|
override val clock: Clock get() = throw UnsupportedOperationException()
|
||||||
override val schedulerService: SchedulerService get() = throw UnsupportedOperationException()
|
override val schedulerService: SchedulerService get() = throw UnsupportedOperationException()
|
||||||
override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {} , Party("MegaCorp", key.public))
|
override val myInfo: NodeInfo get() = NodeInfo(object : SingleMessageRecipient {}, Party("MegaCorp", key.public.tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
class MockIdentityService(val identities: List<Party>) : IdentityService, SingletonSerializeAsToken() {
|
class MockIdentityService(val identities: List<Party>) : IdentityService, SingletonSerializeAsToken() {
|
||||||
private val keyToParties: Map<PublicKey, Party>
|
private val keyToParties: Map<PublicKeyTree, Party>
|
||||||
get() = synchronized(identities) { identities.associateBy { it.owningKey } }
|
get() = synchronized(identities) { identities.associateBy { it.owningKey } }
|
||||||
private val nameToParties: Map<String, Party>
|
private val nameToParties: Map<String, Party>
|
||||||
get() = synchronized(identities) { identities.associateBy { it.name } }
|
get() = synchronized(identities) { identities.associateBy { it.name } }
|
||||||
|
|
||||||
override fun registerIdentity(party: Party) { throw UnsupportedOperationException() }
|
override fun registerIdentity(party: Party) { throw UnsupportedOperationException() }
|
||||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]
|
override fun partyFromKey(key: PublicKeyTree): Party? = keyToParties[key]
|
||||||
override fun partyFromName(name: String): Party? = nameToParties[name]
|
override fun partyFromName(name: String): Party? = nameToParties[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,5 @@
|
|||||||
package net.corda.explorer.views
|
package net.corda.explorer.views
|
||||||
|
|
||||||
import net.corda.client.fxutils.*
|
|
||||||
import net.corda.client.model.*
|
|
||||||
import net.corda.contracts.asset.Cash
|
|
||||||
import net.corda.core.contracts.*
|
|
||||||
import net.corda.core.crypto.SecureHash
|
|
||||||
import net.corda.core.crypto.toStringShort
|
|
||||||
import net.corda.core.node.NodeInfo
|
|
||||||
import net.corda.core.protocols.StateMachineRunId
|
|
||||||
import net.corda.explorer.AmountDiff
|
|
||||||
import net.corda.explorer.formatters.AmountFormatter
|
|
||||||
import net.corda.explorer.identicon.identicon
|
|
||||||
import net.corda.explorer.identicon.identiconToolTip
|
|
||||||
import net.corda.explorer.model.ReportingCurrencyModel
|
|
||||||
import net.corda.explorer.sign
|
|
||||||
import net.corda.explorer.ui.setCustomCellFactory
|
|
||||||
import javafx.beans.binding.Bindings
|
import javafx.beans.binding.Bindings
|
||||||
import javafx.beans.value.ObservableValue
|
import javafx.beans.value.ObservableValue
|
||||||
import javafx.collections.FXCollections
|
import javafx.collections.FXCollections
|
||||||
@ -29,8 +14,23 @@ import javafx.scene.layout.BackgroundFill
|
|||||||
import javafx.scene.layout.BorderPane
|
import javafx.scene.layout.BorderPane
|
||||||
import javafx.scene.layout.CornerRadii
|
import javafx.scene.layout.CornerRadii
|
||||||
import javafx.scene.paint.Color
|
import javafx.scene.paint.Color
|
||||||
|
import net.corda.client.fxutils.*
|
||||||
|
import net.corda.client.model.*
|
||||||
|
import net.corda.contracts.asset.Cash
|
||||||
|
import net.corda.core.contracts.*
|
||||||
|
import net.corda.core.crypto.PublicKeyTree
|
||||||
|
import net.corda.core.crypto.SecureHash
|
||||||
|
import net.corda.core.crypto.tree
|
||||||
|
import net.corda.core.node.NodeInfo
|
||||||
|
import net.corda.core.protocols.StateMachineRunId
|
||||||
|
import net.corda.explorer.AmountDiff
|
||||||
|
import net.corda.explorer.formatters.AmountFormatter
|
||||||
|
import net.corda.explorer.identicon.identicon
|
||||||
|
import net.corda.explorer.identicon.identiconToolTip
|
||||||
|
import net.corda.explorer.model.ReportingCurrencyModel
|
||||||
|
import net.corda.explorer.sign
|
||||||
|
import net.corda.explorer.ui.setCustomCellFactory
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
import java.security.PublicKey
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class TransactionViewer : View() {
|
class TransactionViewer : View() {
|
||||||
@ -136,7 +136,7 @@ class TransactionViewer : View() {
|
|||||||
override val root: Parent by fxml()
|
override val root: Parent by fxml()
|
||||||
private val inputs: ListView<StateNode> by fxid()
|
private val inputs: ListView<StateNode> by fxid()
|
||||||
private val outputs: ListView<StateNode> by fxid()
|
private val outputs: ListView<StateNode> by fxid()
|
||||||
private val signatures: ListView<PublicKey> by fxid()
|
private val signatures: ListView<PublicKeyTree> by fxid()
|
||||||
private val inputPane: TitledPane by fxid()
|
private val inputPane: TitledPane by fxid()
|
||||||
private val outputPane: TitledPane by fxid()
|
private val outputPane: TitledPane by fxid()
|
||||||
private val signaturesPane: TitledPane by fxid()
|
private val signaturesPane: TitledPane by fxid()
|
||||||
@ -148,7 +148,7 @@ class TransactionViewer : View() {
|
|||||||
StateNode(PartiallyResolvedTransaction.InputResolution.Resolved(StateAndRef(transactionState, stateRef)).lift(), stateRef)
|
StateNode(PartiallyResolvedTransaction.InputResolution.Resolved(StateAndRef(transactionState, stateRef)).lift(), stateRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
val signatureData = transaction.transaction.sigs.map { it.by }
|
val signatureData = transaction.transaction.sigs.map { it.by.tree }
|
||||||
// Bind count to TitlePane
|
// Bind count to TitlePane
|
||||||
inputPane.textProperty().bind(inputStates.lift().map { "Input (${it.count()})" })
|
inputPane.textProperty().bind(inputStates.lift().map { "Input (${it.count()})" })
|
||||||
outputPane.textProperty().bind(outputStates.lift().map { "Output (${it.count()})" })
|
outputPane.textProperty().bind(outputStates.lift().map { "Output (${it.count()})" })
|
||||||
@ -172,14 +172,14 @@ class TransactionViewer : View() {
|
|||||||
}
|
}
|
||||||
field("Issuer :") {
|
field("Issuer :") {
|
||||||
label("${data.amount.token.issuer}") {
|
label("${data.amount.token.issuer}") {
|
||||||
tooltip(data.amount.token.issuer.party.owningKey.toStringShort())
|
tooltip(data.amount.token.issuer.party.owningKey.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field("Owner :") {
|
field("Owner :") {
|
||||||
val owner = data.owner
|
val owner = data.owner
|
||||||
val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(owner)
|
val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(owner)
|
||||||
label(nodeInfo?.legalIdentity?.name ?: "???") {
|
label(nodeInfo?.legalIdentity?.name ?: "???") {
|
||||||
tooltip(data.owner.toStringShort())
|
tooltip(data.owner.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,7 +201,7 @@ class TransactionViewer : View() {
|
|||||||
signatures.apply {
|
signatures.apply {
|
||||||
cellFormat { key ->
|
cellFormat { key ->
|
||||||
val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(key)
|
val nodeInfo = Models.get<NetworkIdentityModel>(TransactionViewer::class).lookup(key)
|
||||||
text = "${key.toStringShort()} (${nodeInfo?.legalIdentity?.name ?: "???"})"
|
text = "$key (${nodeInfo?.legalIdentity?.name ?: "???"})"
|
||||||
}
|
}
|
||||||
prefHeight = 185.0
|
prefHeight = 185.0
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user