Kryo serialisation whitelisting and misc enhancements. (#267)

Kryo serialisation whitelisting and misc enhancements
This commit is contained in:
Rick Parker 2017-02-28 08:12:18 +00:00 committed by GitHub
parent 3d04c91e61
commit c4c4c51d7d
103 changed files with 995 additions and 376 deletions

View File

@ -9,6 +9,7 @@ import com.google.common.io.ByteStreams
import com.google.common.util.concurrent.* import com.google.common.util.concurrent.*
import kotlinx.support.jdk7.use import kotlinx.support.jdk7.use
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.serialization.CordaSerializable
import org.slf4j.Logger import org.slf4j.Logger
import rx.Observable import rx.Observable
import rx.Observer import rx.Observer
@ -257,6 +258,7 @@ class ThreadBox<out T>(val content: T, val lock: ReentrantLock = ReentrantLock()
* *
* We avoid the use of the word transient here to hopefully reduce confusion with the term in relation to (Java) serialization. * We avoid the use of the word transient here to hopefully reduce confusion with the term in relation to (Java) serialization.
*/ */
@CordaSerializable
abstract class RetryableException(message: String) : Exception(message) abstract class RetryableException(message: String) : Exception(message)
/** /**
@ -307,6 +309,7 @@ fun extractZipFile(zipFile: Path, toDirectory: Path) {
val Throwable.rootCause: Throwable get() = Throwables.getRootCause(this) val Throwable.rootCause: Throwable get() = Throwables.getRootCause(this)
/** Representation of an operation that may have thrown an error. */ /** Representation of an operation that may have thrown an error. */
@CordaSerializable
data class ErrorOr<out A> private constructor(val value: A?, val error: Throwable?) { data class ErrorOr<out A> private constructor(val value: A?, val error: Throwable?) {
// The ErrorOr holds a value iff error == null // The ErrorOr holds a value iff error == null
constructor(value: A) : this(value, null) constructor(value: A) : this(value, null)

View File

@ -9,6 +9,7 @@ import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal import java.math.BigDecimal
import java.math.BigInteger import java.math.BigInteger
import java.time.DayOfWeek import java.time.DayOfWeek
@ -34,6 +35,7 @@ import java.util.*
* *
* @param T the type of the token, for example [Currency]. * @param T the type of the token, for example [Currency].
*/ */
@CordaSerializable
data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> { data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
companion object { companion object {
/** /**
@ -108,12 +110,14 @@ fun <T> Iterable<Amount<T>>.sumOrZero(currency: T) = if (iterator().hasNext()) s
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** A [FixOf] identifies the question side of a fix: what day, tenor and type of fix ("LIBOR", "EURIBOR" etc) */ /** A [FixOf] identifies the question side of a fix: what day, tenor and type of fix ("LIBOR", "EURIBOR" etc) */
@CordaSerializable
data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Tenor) data class FixOf(val name: String, val forDay: LocalDate, val ofTenor: Tenor)
/** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */ /** A [Fix] represents a named interest rate, on a given day, for a given duration. It can be embedded in a tx. */
data class Fix(val of: FixOf, val value: BigDecimal) : CommandData data class Fix(val of: FixOf, val value: BigDecimal) : CommandData
/** Represents a textual expression of e.g. a formula */ /** Represents a textual expression of e.g. a formula */
@CordaSerializable
@JsonDeserialize(using = ExpressionDeserializer::class) @JsonDeserialize(using = ExpressionDeserializer::class)
@JsonSerialize(using = ExpressionSerializer::class) @JsonSerialize(using = ExpressionSerializer::class)
data class Expression(val expr: String) data class Expression(val expr: String)
@ -131,6 +135,7 @@ object ExpressionDeserializer : JsonDeserializer<Expression>() {
} }
/** Placeholder class for the Tenor datatype - which is a standardised duration of time until maturity */ /** Placeholder class for the Tenor datatype - which is a standardised duration of time until maturity */
@CordaSerializable
data class Tenor(val name: String) { data class Tenor(val name: String) {
private val amount: Int private val amount: Int
private val unit: TimeUnit private val unit: TimeUnit
@ -166,6 +171,7 @@ data class Tenor(val name: String) {
override fun toString(): String = name override fun toString(): String = name
@CordaSerializable
enum class TimeUnit(val code: String) { enum class TimeUnit(val code: String) {
Day("D"), Week("W"), Month("M"), Year("Y") Day("D"), Week("W"), Month("M"), Year("Y")
} }
@ -175,6 +181,7 @@ data class Tenor(val name: String) {
* Simple enum for returning accurals adjusted or unadjusted. * Simple enum for returning accurals adjusted or unadjusted.
* We don't actually do anything with this yet though, so it's ignored for now. * We don't actually do anything with this yet though, so it's ignored for now.
*/ */
@CordaSerializable
enum class AccrualAdjustment { enum class AccrualAdjustment {
Adjusted, Unadjusted Adjusted, Unadjusted
} }
@ -183,6 +190,7 @@ enum class AccrualAdjustment {
* This is utilised in the [DateRollConvention] class to determine which way we should initially step when * This is utilised in the [DateRollConvention] class to determine which way we should initially step when
* finding a business day. * finding a business day.
*/ */
@CordaSerializable
enum class DateRollDirection(val value: Long) { FORWARD(1), BACKWARD(-1) } enum class DateRollDirection(val value: Long) { FORWARD(1), BACKWARD(-1) }
/** /**
@ -190,6 +198,7 @@ enum class DateRollDirection(val value: Long) { FORWARD(1), BACKWARD(-1) }
* Depending on the accounting requirement, we can move forward until we get to a business day, or backwards. * Depending on the accounting requirement, we can move forward until we get to a business day, or backwards.
* There are some additional rules which are explained in the individual cases below. * There are some additional rules which are explained in the individual cases below.
*/ */
@CordaSerializable
enum class DateRollConvention { enum class DateRollConvention {
// direction() cannot be a val due to the throw in the Actual instance // direction() cannot be a val due to the throw in the Actual instance
@ -235,6 +244,7 @@ enum class DateRollConvention {
* Note that the first character cannot be a number (enum naming constraints), so we drop that * Note that the first character cannot be a number (enum naming constraints), so we drop that
* in the toString lest some people get confused. * in the toString lest some people get confused.
*/ */
@CordaSerializable
enum class DayCountBasisDay { enum class DayCountBasisDay {
// We have to prefix 30 etc with a letter due to enum naming constraints. // We have to prefix 30 etc with a letter due to enum naming constraints.
D30, D30,
@ -246,6 +256,7 @@ enum class DayCountBasisDay {
} }
/** This forms the year part of the "Day Count Basis" used for interest calculation. */ /** This forms the year part of the "Day Count Basis" used for interest calculation. */
@CordaSerializable
enum class DayCountBasisYear { enum class DayCountBasisYear {
// Ditto above comment for years. // Ditto above comment for years.
Y360, Y360,
@ -257,6 +268,7 @@ enum class DayCountBasisYear {
} }
/** Whether the payment should be made before the due date, or after it. */ /** Whether the payment should be made before the due date, or after it. */
@CordaSerializable
enum class PaymentRule { enum class PaymentRule {
InAdvance, InArrears, InAdvance, InArrears,
} }
@ -266,6 +278,7 @@ enum class PaymentRule {
* that would divide into (eg annually = 1, semiannual = 2, monthly = 12 etc). * that would divide into (eg annually = 1, semiannual = 2, monthly = 12 etc).
*/ */
@Suppress("unused") // TODO: Revisit post-Vega and see if annualCompoundCount is still needed. @Suppress("unused") // TODO: Revisit post-Vega and see if annualCompoundCount is still needed.
@CordaSerializable
enum class Frequency(val annualCompoundCount: Int) { enum class Frequency(val annualCompoundCount: Int) {
Annual(1) { Annual(1) {
override fun offset(d: LocalDate, n: Long) = d.plusYears(1 * n) override fun offset(d: LocalDate, n: Long) = d.plusYears(1 * n)
@ -304,7 +317,9 @@ fun LocalDate.isWorkingDay(accordingToCalendar: BusinessCalendar): Boolean = acc
* typical feature of financial contracts, in which a business may not want a payment event to fall on a day when * typical feature of financial contracts, in which a business may not want a payment event to fall on a day when
* no staff are around to handle problems. * no staff are around to handle problems.
*/ */
@CordaSerializable
open class BusinessCalendar private constructor(val holidayDates: List<LocalDate>) { open class BusinessCalendar private constructor(val holidayDates: List<LocalDate>) {
@CordaSerializable
class UnknownCalendar(name: String) : Exception("$name not found") class UnknownCalendar(name: String) : Exception("$name not found")
companion object { companion object {
@ -434,6 +449,7 @@ fun calculateDaysBetween(startDate: LocalDate,
* Enum for the types of netting that can be applied to state objects. Exact behaviour * Enum for the types of netting that can be applied to state objects. Exact behaviour
* for each type of netting is left to the contract to determine. * for each type of netting is left to the contract to determine.
*/ */
@CordaSerializable
enum class NetType { enum class NetType {
/** /**
* Close-out netting applies where one party is bankrupt or otherwise defaults (exact terms are contract specific), * Close-out netting applies where one party is bankrupt or otherwise defaults (exact terms are contract specific),
@ -461,6 +477,7 @@ enum class NetType {
* @param defaultFractionDigits the number of digits normally after the decimal point when referring to quantities of * @param defaultFractionDigits the number of digits normally after the decimal point when referring to quantities of
* this commodity. * this commodity.
*/ */
@CordaSerializable
data class Commodity(val commodityCode: String, data class Commodity(val commodityCode: String,
val displayName: String, val displayName: String,
val defaultFractionDigits: Int = 0) { val defaultFractionDigits: Int = 0) {
@ -487,6 +504,7 @@ data class Commodity(val commodityCode: String,
* So that the first time a state is issued this should be given a new UUID. * So that the first time a state is issued this should be given a new UUID.
* Subsequent copies and evolutions of a state should just copy the [externalId] and [id] fields unmodified. * Subsequent copies and evolutions of a state should just copy the [externalId] and [id] fields unmodified.
*/ */
@CordaSerializable
data class UniqueIdentifier(val externalId: String? = null, val id: UUID = UUID.randomUUID()) : Comparable<UniqueIdentifier> { data class UniqueIdentifier(val externalId: String? = null, val id: UUID = UUID.randomUUID()) : Comparable<UniqueIdentifier> {
override fun toString(): String = if (externalId != null) "${externalId}_$id" else id.toString() override fun toString(): String = if (externalId != null) "${externalId}_$id" else id.toString()

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogicRef import net.corda.core.flows.FlowLogicRef
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
@ -62,6 +63,7 @@ interface NettableState<N : BilateralNettableState<N>, T : Any> : BilateralNetta
* notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states * notary is responsible for ensuring there is no "double spending" by only signing a transaction if the input states
* are all free. * are all free.
*/ */
@CordaSerializable
interface ContractState { interface ContractState {
/** /**
* An instance of the contract class that will verify this state. * An instance of the contract class that will verify this state.
@ -121,6 +123,7 @@ interface ContractState {
* A wrapper for [ContractState] containing additional platform-level state information. * A wrapper for [ContractState] containing additional platform-level state information.
* This is the definitive state that is stored on the ledger and used in transaction outputs. * This is the definitive state that is stored on the ledger and used in transaction outputs.
*/ */
@CordaSerializable
data class TransactionState<out T : ContractState> @JvmOverloads constructor( data class TransactionState<out T : ContractState> @JvmOverloads constructor(
/** The custom contract state */ /** The custom contract state */
val data: T, val data: T,
@ -169,6 +172,7 @@ interface IssuanceDefinition
* *
* @param P the type of product underlying the definition, for example [Currency]. * @param P the type of product underlying the definition, for example [Currency].
*/ */
@CordaSerializable
data class Issued<out P>(val issuer: PartyAndReference, val product: P) { data class Issued<out P>(val issuer: PartyAndReference, val product: P) {
override fun toString() = "$product issued by $issuer" override fun toString() = "$product issued by $issuer"
} }
@ -239,6 +243,7 @@ interface LinearState : ContractState {
/** /**
* Standard clause to verify the LinearState safety properties. * Standard clause to verify the LinearState safety properties.
*/ */
@CordaSerializable
class ClauseVerifier<S : LinearState, C : CommandData>() : Clause<S, C, Unit>() { class ClauseVerifier<S : LinearState, C : CommandData>() : Clause<S, C, Unit>() {
override fun verify(tx: TransactionForContract, override fun verify(tx: TransactionForContract,
inputs: List<S>, inputs: List<S>,
@ -334,11 +339,13 @@ fun ContractState.hash(): SecureHash = SecureHash.sha256(serialize().bytes)
* A stateref is a pointer (reference) to a state, this is an equivalent of an "outpoint" in Bitcoin. It records which * A stateref is a pointer (reference) to a state, this is an equivalent of an "outpoint" in Bitcoin. It records which
* transaction defined the state and where in that transaction it was. * transaction defined the state and where in that transaction it was.
*/ */
@CordaSerializable
data class StateRef(val txhash: SecureHash, val index: Int) { data class StateRef(val txhash: SecureHash, val index: Int) {
override fun toString() = "$txhash($index)" override fun toString() = "$txhash($index)"
} }
/** A StateAndRef is simply a (state, ref) pair. For instance, a vault (which holds available assets) contains these. */ /** A StateAndRef is simply a (state, ref) pair. For instance, a vault (which holds available assets) contains these. */
@CordaSerializable
data class StateAndRef<out T : ContractState>(val state: TransactionState<T>, val ref: StateRef) data class StateAndRef<out T : ContractState>(val state: TransactionState<T>, val ref: StateRef)
/** Filters a list of [StateAndRef] objects according to the type of the states */ /** Filters a list of [StateAndRef] objects according to the type of the states */
@ -350,12 +357,14 @@ inline fun <reified T : ContractState> Iterable<StateAndRef<ContractState>>.filt
* Reference to something being stored or issued by a party e.g. in a vault or (more likely) on their normal * Reference to something being stored or issued by a party e.g. in a vault or (more likely) on their normal
* ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party. * ledger. The reference is intended to be encrypted so it's meaningless to anyone other than the party.
*/ */
@CordaSerializable
data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) { data class PartyAndReference(val party: AnonymousParty, val reference: OpaqueBytes) {
constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), reference) constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), reference)
override fun toString() = "${party}$reference" override fun toString() = "${party}$reference"
} }
/** Marker interface for classes that represent commands */ /** Marker interface for classes that represent commands */
@CordaSerializable
interface CommandData interface CommandData
/** Commands that inherit from this are intended to have no data items: it's only their presence that matters. */ /** Commands that inherit from this are intended to have no data items: it's only their presence that matters. */
@ -365,6 +374,7 @@ 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 */
@CordaSerializable
data class Command(val value: CommandData, val signers: List<CompositeKey>) { data class Command(val value: CommandData, val signers: List<CompositeKey>) {
init { init {
require(signers.isNotEmpty()) require(signers.isNotEmpty())
@ -402,6 +412,7 @@ interface NetCommand : CommandData {
data class UpgradeCommand(val upgradedContractClass: Class<out UpgradedContract<*, *>>) : CommandData data class UpgradeCommand(val upgradedContractClass: Class<out UpgradedContract<*, *>>) : 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. */
@CordaSerializable
data class AuthenticatedObject<out T : Any>( data class AuthenticatedObject<out T : Any>(
val signers: List<CompositeKey>, val signers: List<CompositeKey>,
/** 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 */
@ -413,6 +424,7 @@ data class AuthenticatedObject<out T : Any>(
* If present in a transaction, contains a time that was verified by the uniqueness service. The true time must be * If present in a transaction, contains a time that was verified by the uniqueness service. The true time must be
* between (after, before). * between (after, before).
*/ */
@CordaSerializable
data class Timestamp(val after: Instant?, val before: Instant?) { data class Timestamp(val after: Instant?, val before: Instant?) {
init { init {
if (after == null && before == null) if (after == null && before == null)
@ -431,7 +443,10 @@ data class Timestamp(val after: Instant?, val before: Instant?) {
* every [LedgerTransaction] they see on the network, for every input and output state. All contracts must accept the * every [LedgerTransaction] they see on the network, for every input and output state. All contracts must accept the
* transaction for it to be accepted: failure of any aborts the entire thing. The time is taken from a trusted * transaction for it to be accepted: failure of any aborts the entire thing. The time is taken from a trusted
* timestamp attached to the transaction itself i.e. it is NOT necessarily the current time. * timestamp attached to the transaction itself i.e. it is NOT necessarily the current time.
*
* TODO: Contract serialization is likely to change, so the annotation is likely temporary.
*/ */
@CordaSerializable
interface Contract { interface Contract {
/** /**
* Takes an object that represents a state transition, and ensures the inputs/outputs/commands make sense. * Takes an object that represents a state transition, and ensures the inputs/outputs/commands make sense.

View File

@ -2,10 +2,12 @@ package net.corda.core.contracts
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
/** Defines transaction build & validation logic for a specific transaction type */ /** Defines transaction build & validation logic for a specific transaction type */
@CordaSerializable
sealed class TransactionType { sealed class TransactionType {
override fun equals(other: Any?) = other?.javaClass == javaClass override fun equals(other: Any?) = other?.javaClass == javaClass
override fun hashCode() = javaClass.name.hashCode() override fun hashCode() = javaClass.name.hashCode()

View File

@ -4,6 +4,7 @@ import net.corda.core.crypto.CompositeKey
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.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import java.util.* import java.util.*
@ -94,6 +95,7 @@ class AttachmentResolutionException(val hash : SecureHash) : FlowException() {
override fun toString(): String = "Attachment resolution failure for $hash" override fun toString(): String = "Attachment resolution failure for $hash"
} }
@CordaSerializable
class TransactionConflictException(val conflictRef: StateRef, val tx1: LedgerTransaction, val tx2: LedgerTransaction) : Exception() class TransactionConflictException(val conflictRef: StateRef, val tx1: LedgerTransaction, val tx2: LedgerTransaction) : Exception()
sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : FlowException(cause) { sealed class TransactionVerificationException(val tx: LedgerTransaction, cause: Throwable?) : FlowException(cause) {
@ -116,6 +118,8 @@ sealed class TransactionVerificationException(val tx: LedgerTransaction, cause:
class TransactionMissingEncumbranceException(tx: LedgerTransaction, val missing: Int, val inOut: Direction) : TransactionVerificationException(tx, null) { class TransactionMissingEncumbranceException(tx: LedgerTransaction, val missing: Int, val inOut: Direction) : TransactionVerificationException(tx, null) {
override val message: String get() = "Missing required encumbrance $missing in $inOut" override val message: String get() = "Missing required encumbrance $missing in $inOut"
} }
@CordaSerializable
enum class Direction { enum class Direction {
INPUT, INPUT,
OUTPUT OUTPUT

View File

@ -1,6 +1,7 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import java.security.PublicKey import java.security.PublicKey
@ -8,6 +9,7 @@ import java.security.PublicKey
* An [AbstractParty] contains the common elements of [Party] and [AnonymousParty], specifically the owning key of * An [AbstractParty] contains the common elements of [Party] and [AnonymousParty], specifically the owning key of
* the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case. * the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case.
*/ */
@CordaSerializable
abstract class AbstractParty(val owningKey: CompositeKey) { abstract class AbstractParty(val owningKey: CompositeKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */ /** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
constructor(owningKey: PublicKey) : this(owningKey.composite) constructor(owningKey: PublicKey) : this(owningKey.composite)

View File

@ -2,6 +2,7 @@ package net.corda.core.crypto
import net.corda.core.crypto.CompositeKey.Leaf import net.corda.core.crypto.CompositeKey.Leaf
import net.corda.core.crypto.CompositeKey.Node import net.corda.core.crypto.CompositeKey.Node
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import java.security.PublicKey import java.security.PublicKey
@ -19,6 +20,7 @@ import java.security.PublicKey
* Using these constructs we can express e.g. 1 of N (OR) or N of N (AND) signature requirements. By nesting we can * Using these constructs we can express e.g. 1 of N (OR) or N of N (AND) signature requirements. By nesting we can
* create multi-level requirements such as *"either the CEO or 3 of 5 of his assistants need to sign"*. * create multi-level requirements such as *"either the CEO or 3 of 5 of his assistants need to sign"*.
*/ */
@CordaSerializable
sealed class CompositeKey { sealed class CompositeKey {
/** Checks whether [keys] match a sufficient amount of leaf nodes */ /** Checks whether [keys] match a sufficient amount of leaf nodes */
abstract fun isFulfilledBy(keys: Iterable<PublicKey>): Boolean abstract fun isFulfilledBy(keys: Iterable<PublicKey>): Boolean

View File

@ -2,6 +2,7 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
@ -26,6 +27,7 @@ fun newSecureRandom(): SecureRandom {
* signature. It isn't used currently, but experience from Bitcoin suggests such a feature is useful, especially when * signature. It isn't used currently, but experience from Bitcoin suggests such a feature is useful, especially when
* building partially signed transactions. * building partially signed transactions.
*/ */
@CordaSerializable
open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) { open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
/** A digital signature that identifies who the public key is owned by. */ /** A digital signature that identifies who the public key is owned by. */
open class WithKey(val by: PublicKey, bits: ByteArray) : DigitalSignature(bits) { open class WithKey(val by: PublicKey, bits: ByteArray) : DigitalSignature(bits) {
@ -37,6 +39,7 @@ open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey.singleKey, bits) class LegallyIdentifiable(val signer: Party, bits: ByteArray) : WithKey(signer.owningKey.singleKey, bits)
} }
@CordaSerializable
object NullPublicKey : PublicKey, Comparable<PublicKey> { object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "NULL" override fun getAlgorithm() = "NULL"
override fun getEncoded() = byteArrayOf(0) override fun getEncoded() = byteArrayOf(0)
@ -48,6 +51,7 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
val NullCompositeKey = NullPublicKey.composite val NullCompositeKey = NullPublicKey.composite
// TODO: Clean up this duplication between Null and Dummy public key // TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> { class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "DUMMY" override fun getAlgorithm() = "DUMMY"
override fun getEncoded() = s.toByteArray() override fun getEncoded() = s.toByteArray()
@ -59,6 +63,7 @@ class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
} }
/** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */ /** A signature with a key and value of zero. Useful when you want a signature object that you know won't ever be used. */
@CordaSerializable
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32)) object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
/** Utility to simplify the act of signing a byte array */ /** Utility to simplify the act of signing a byte array */

View File

@ -1,10 +1,10 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.SecureHash.Companion.zeroHash import net.corda.core.crypto.SecureHash.Companion.zeroHash
import net.corda.core.serialization.CordaSerializable
import java.util.* import java.util.*
@CordaSerializable
class MerkleTreeException(val reason: String) : Exception() { class MerkleTreeException(val reason: String) : Exception() {
override fun toString() = "Partial Merkle Tree exception. Reason: $reason" override fun toString() = "Partial Merkle Tree exception. Reason: $reason"
} }
@ -43,7 +43,7 @@ class MerkleTreeException(val reason: String) : Exception() {
* (there can be a difference in obtained leaves ordering - that's why it's a set comparison not hashing leaves into a tree). * (there can be a difference in obtained leaves ordering - that's why it's a set comparison not hashing leaves into a tree).
* If both equalities hold, we can assume that l3 and l5 belong to the transaction with root h15. * If both equalities hold, we can assume that l3 and l5 belong to the transaction with root h15.
*/ */
@CordaSerializable
class PartialMerkleTree(val root: PartialTree) { class PartialMerkleTree(val root: PartialTree) {
/** /**
* The structure is a little different than that of Merkle Tree. * The structure is a little different than that of Merkle Tree.
@ -52,6 +52,7 @@ class PartialMerkleTree(val root: PartialTree) {
* transaction and leaves that just keep hashes needed for calculation. Reason for this approach: during verification * transaction and leaves that just keep hashes needed for calculation. Reason for this approach: during verification
* it's easier to extract hashes used as a base for this tree. * it's easier to extract hashes used as a base for this tree.
*/ */
@CordaSerializable
sealed class PartialTree { sealed class PartialTree {
class IncludedLeaf(val hash: SecureHash) : PartialTree() class IncludedLeaf(val hash: SecureHash) : PartialTree()
class Leaf(val hash: SecureHash) : PartialTree() class Leaf(val hash: SecureHash) : PartialTree()

View File

@ -1,6 +1,7 @@
package net.corda.core.crypto package net.corda.core.crypto
import com.google.common.io.BaseEncoding import com.google.common.io.BaseEncoding
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import java.security.MessageDigest import java.security.MessageDigest
@ -8,6 +9,7 @@ import java.security.MessageDigest
* Container for a cryptographically secure hash value. * Container for a cryptographically secure hash value.
* Provides utilities for generating a cryptographic hash using different algorithms (currently only SHA-256 supported). * Provides utilities for generating a cryptographic hash using different algorithms (currently only SHA-256 supported).
*/ */
@CordaSerializable
sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) { sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
/** SHA-256 is part of the SHA-2 hash function family. Generated hash is fixed size, 256-bits (32-bytes) */ /** SHA-256 is part of the SHA-2 hash function family. Generated hash is fixed size, 256-bits (32-bytes) */
class SHA256(bytes: ByteArray) : SecureHash(bytes) { class SHA256(bytes: ByteArray) : SecureHash(bytes) {

View File

@ -1,5 +1,6 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import java.security.SignatureException import java.security.SignatureException
@ -11,6 +12,7 @@ import java.security.SignatureException
* @param raw the raw serialized data. * @param raw the raw serialized data.
* @param sig the (unverified) signature for the data. * @param sig the (unverified) signature for the data.
*/ */
@CordaSerializable
open class SignedData<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSignature.WithKey) { open class SignedData<T : Any>(val raw: SerializedBytes<T>, val sig: DigitalSignature.WithKey) {
/** /**
* Return the deserialized data if the signature can be verified. * Return the deserialized data if the signature can be verified.

View File

@ -1,5 +1,7 @@
package net.corda.core.flows package net.corda.core.flows
import net.corda.core.serialization.CordaSerializable
/** /**
* Exception which can be thrown by a [FlowLogic] at any point in its logic to unexpectedly bring it to a permanent end. * Exception which can be thrown by a [FlowLogic] at any point in its logic to unexpectedly bring it to a permanent end.
* The exception will propagate to all counterparty flows and will be thrown on their end the next time they wait on a * The exception will propagate to all counterparty flows and will be thrown on their end the next time they wait on a
@ -9,6 +11,7 @@ package net.corda.core.flows
* [FlowException] (or a subclass) can be a valid expected response from a flow, particularly ones which act as a service. * [FlowException] (or a subclass) can be a valid expected response from a flow, particularly ones which act as a service.
* It is recommended a [FlowLogic] document the [FlowException] types it can throw. * It is recommended a [FlowLogic] document the [FlowException] types it can throw.
*/ */
@CordaSerializable
open class FlowException(override val message: String?, override val cause: Throwable?) : Exception() { open class FlowException(override val message: String?, override val cause: Throwable?) : Exception() {
constructor(message: String?) : this(message, null) constructor(message: String?) : this(message, null)
constructor(cause: Throwable?) : this(cause?.toString(), cause) constructor(cause: Throwable?) : this(cause?.toString(), cause)

View File

@ -2,6 +2,7 @@ package net.corda.core.flows
import com.google.common.primitives.Primitives import com.google.common.primitives.Primitives
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type import java.lang.reflect.Type
@ -186,6 +187,7 @@ class FlowLogicRefFactory(private val flowWhitelist: Map<String, Set<String>>) :
} }
} }
@CordaSerializable
class IllegalFlowLogicException(type: Class<*>, msg: String) : IllegalArgumentException("${FlowLogicRef::class.java.simpleName} cannot be constructed for ${FlowLogic::class.java.simpleName} of type ${type.name} $msg") class IllegalFlowLogicException(type: Class<*>, msg: String) : IllegalArgumentException("${FlowLogicRef::class.java.simpleName} cannot be constructed for ${FlowLogic::class.java.simpleName} of type ${type.name} $msg")
/** /**
@ -194,12 +196,14 @@ class IllegalFlowLogicException(type: Class<*>, msg: String) : IllegalArgumentEx
* Only allows a String reference to the FlowLogic class, and only allows restricted argument types as per [FlowLogicRefFactory]. * Only allows a String reference to the FlowLogic class, and only allows restricted argument types as per [FlowLogicRefFactory].
*/ */
// TODO: align this with the existing [FlowRef] in the bank-side API (probably replace some of the API classes) // TODO: align this with the existing [FlowRef] in the bank-side API (probably replace some of the API classes)
@CordaSerializable
data class FlowLogicRef internal constructor(val flowLogicClassName: String, val appContext: AppContext, val args: Map<String, Any?>) data class FlowLogicRef internal constructor(val flowLogicClassName: String, val appContext: AppContext, val args: Map<String, Any?>)
/** /**
* This is just some way to track what attachments need to be in the class loader, but may later include some app * This is just some way to track what attachments need to be in the class loader, but may later include some app
* properties loaded from the attachments. And perhaps the authenticated user for an API call? * properties loaded from the attachments. And perhaps the authenticated user for an API call?
*/ */
@CordaSerializable
data class AppContext(val attachments: List<SecureHash>) { data class AppContext(val attachments: List<SecureHash>) {
// TODO: build a real [AttachmentsClassLoader] etc // TODO: build a real [AttachmentsClassLoader] etc
val classLoader: ClassLoader val classLoader: ClassLoader

View File

@ -5,6 +5,7 @@ import com.google.common.util.concurrent.ListenableFuture
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.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
import org.slf4j.Logger import org.slf4j.Logger
@ -14,6 +15,7 @@ import java.util.*
* A unique identifier for a single state machine run, valid across node restarts. Note that a single run always * A unique identifier for a single state machine run, valid across node restarts. Note that a single run always
* has at least one flow, but that flow may also invoke sub-flows: they all share the same run id. * has at least one flow, but that flow may also invoke sub-flows: they all share the same run id.
*/ */
@CordaSerializable
data class StateMachineRunId private constructor(val uuid: UUID) { data class StateMachineRunId private constructor(val uuid: UUID) {
companion object { companion object {
fun createRandom(): StateMachineRunId = StateMachineRunId(UUID.randomUUID()) fun createRandom(): StateMachineRunId = StateMachineRunId(UUID.randomUUID())

View File

@ -14,23 +14,26 @@ import net.corda.core.node.NodeInfo
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.StateMachineTransactionMapping import net.corda.core.node.services.StateMachineTransactionMapping
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import rx.Observable import rx.Observable
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@CordaSerializable
data class StateMachineInfo( data class StateMachineInfo(
val id: StateMachineRunId, val id: StateMachineRunId,
val flowLogicClassName: String, val flowLogicClassName: String,
val progressTrackerStepAndUpdates: Pair<String, Observable<String>>? val progressTrackerStepAndUpdates: Pair<String, Observable<String>>?
) )
@CordaSerializable
sealed class StateMachineUpdate(val id: StateMachineRunId) { sealed class StateMachineUpdate(val id: StateMachineRunId) {
class Added(val stateMachineInfo: StateMachineInfo) : StateMachineUpdate(stateMachineInfo.id) { class Added(val stateMachineInfo: StateMachineInfo) : StateMachineUpdate(stateMachineInfo.id) {
override fun toString() = "Added($id, ${stateMachineInfo.flowLogicClassName})" override fun toString() = "Added($id, ${stateMachineInfo.flowLogicClassName})"
} }
class Removed(id: StateMachineRunId) : StateMachineUpdate(id) { class Removed(id: StateMachineRunId) : StateMachineUpdate(id) {
override fun toString() = "Removed($id)" override fun toString() = "Removed($id)"
} }
@ -212,6 +215,7 @@ inline fun <T : Any, A, B, C, D, reified R : FlowLogic<T>> CordaRPCOps.startFlow
* @param progress The stream of progress tracker events. * @param progress The stream of progress tracker events.
* @param returnValue A [ListenableFuture] of the flow's return value. * @param returnValue A [ListenableFuture] of the flow's return value.
*/ */
@CordaSerializable
data class FlowHandle<A>( data class FlowHandle<A>(
val id: StateMachineRunId, val id: StateMachineRunId,
val progress: Observable<String>, val progress: Observable<String>,

View File

@ -5,6 +5,7 @@ import com.google.common.util.concurrent.SettableFuture
import net.corda.core.catch import net.corda.core.catch
import net.corda.core.node.services.DEFAULT_SESSION_ID import net.corda.core.node.services.DEFAULT_SESSION_ID
import net.corda.core.node.services.PartyInfo import net.corda.core.node.services.PartyInfo
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.DeserializeAsKotlinObjectDef import net.corda.core.serialization.DeserializeAsKotlinObjectDef
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -160,6 +161,7 @@ interface MessageHandlerRegistration
* @param sessionID identifier for the session the message is part of. For services listening before * @param sessionID identifier for the session the message is part of. For services listening before
* a session is established, use [DEFAULT_SESSION_ID]. * a session is established, use [DEFAULT_SESSION_ID].
*/ */
@CordaSerializable
data class TopicSession(val topic: String, val sessionID: Long = DEFAULT_SESSION_ID) { data class TopicSession(val topic: String, val sessionID: Long = DEFAULT_SESSION_ID) {
fun isBlank() = topic.isBlank() && sessionID == DEFAULT_SESSION_ID fun isBlank() = topic.isBlank() && sessionID == DEFAULT_SESSION_ID
override fun toString(): String = "$topic.$sessionID" override fun toString(): String = "$topic.$sessionID"
@ -213,4 +215,5 @@ interface AllPossibleRecipients : MessageRecipients
* A general Ack message that conveys no content other than it's presence for use when you want an acknowledgement * A general Ack message that conveys no content other than it's presence for use when you want an acknowledgement
* from a recipient. Using [Unit] can be ambiguous as it is similar to [Void] and so could mean no response. * from a recipient. Using [Unit] can be ambiguous as it is similar to [Void] and so could mean no response.
*/ */
@CordaSerializable
object Ack : DeserializeAsKotlinObjectDef object Ack : DeserializeAsKotlinObjectDef

View File

@ -2,6 +2,7 @@ package net.corda.core.node
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.CordaSerializable
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.FileNotFoundException import java.io.FileNotFoundException
@ -24,6 +25,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>, parent: ClassLoader
private val pathsToAttachments = HashMap<String, Attachment>() private val pathsToAttachments = HashMap<String, Attachment>()
private val idsToAttachments = HashMap<SecureHash, Attachment>() private val idsToAttachments = HashMap<SecureHash, Attachment>()
@CordaSerializable
class OverlappingAttachments(val path: String) : Exception() { class OverlappingAttachments(val path: String) : Exception() {
override fun toString() = "Multiple attachments define a file at path $path" override fun toString() = "Multiple attachments define a file at path $path"
} }

View File

@ -1,7 +1,7 @@
package net.corda.core.node package net.corda.core.node
import com.esotericsoftware.kryo.Kryo
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.serialization.SerializationCustomization
import java.util.function.Function import java.util.function.Function
/** /**
@ -40,14 +40,12 @@ abstract class CordaPluginRegistry(
open val servicePlugins: List<Function<PluginServiceHub, out Any>> = emptyList() open val servicePlugins: List<Function<PluginServiceHub, out Any>> = emptyList()
) { ) {
/** /**
* Optionally register types with [Kryo] for use over RPC, as we lock down the types that can be serialised in this * Optionally whitelist types for use in object serialization, as we lock down the types that can be serialized.
* particular use case.
* For example, if you add an RPC interface that carries some contract states back and forth, you need to register
* those classes here using the [register] method on Kryo.
*
* TODO: Kryo and likely the requirement to register classes here will go away when we replace the serialization implementation.
* *
* For example, if you add a new [ContractState] it needs to be whitelisted. You can do that either by
* adding the @CordaSerializable annotation or via this method.
**
* @return true if you register types, otherwise you will be filtered out of the list of plugins considered in future. * @return true if you register types, otherwise you will be filtered out of the list of plugins considered in future.
*/ */
open fun registerRPCKryoTypes(kryo: Kryo): Boolean = false open fun customizeSerialization(custom: SerializationCustomization): Boolean = false
} }

View File

@ -4,16 +4,19 @@ import net.corda.core.crypto.Party
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
/** /**
* Information for an advertised service including the service specific identity information. * Information for an advertised service including the service specific identity information.
* The identity can be used in flows and is distinct from the Node's legalIdentity * The identity can be used in flows and is distinct from the Node's legalIdentity
*/ */
@CordaSerializable
data class ServiceEntry(val info: ServiceInfo, val identity: Party) data class ServiceEntry(val info: ServiceInfo, val identity: Party)
/** /**
* Info about a network node that acts on behalf of some form of contract party. * Info about a network node that acts on behalf of some form of contract party.
*/ */
@CordaSerializable
data class NodeInfo(val address: SingleMessageRecipient, data class NodeInfo(val address: SingleMessageRecipient,
val legalIdentity: Party, val legalIdentity: Party,
var advertisedServices: List<ServiceEntry> = emptyList(), var advertisedServices: List<ServiceEntry> = emptyList(),

View File

@ -1,8 +1,10 @@
package net.corda.core.node package net.corda.core.node
import net.corda.core.serialization.CordaSerializable
import java.util.* import java.util.*
/** A latitude/longitude pair. */ /** A latitude/longitude pair. */
@CordaSerializable
data class WorldCoordinate(val latitude: Double, val longitude: Double) { data class WorldCoordinate(val latitude: Double, val longitude: Double) {
init { init {
require(latitude in -90..90) require(latitude in -90..90)
@ -39,6 +41,7 @@ data class WorldCoordinate(val latitude: Double, val longitude: Double) {
* A labelled [WorldCoordinate], where the label is human meaningful. For example, the name of the nearest city. * A labelled [WorldCoordinate], where the label is human meaningful. For example, the name of the nearest city.
* Labels should not refer to non-landmarks, for example, they should not contain the names of organisations. * Labels should not refer to non-landmarks, for example, they should not contain the names of organisations.
*/ */
@CordaSerializable
data class PhysicalLocation(val coordinate: WorldCoordinate, val description: String) data class PhysicalLocation(val coordinate: WorldCoordinate, val description: String)
/** /**

View File

@ -9,6 +9,7 @@ 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 net.corda.core.randomOrNull import net.corda.core.randomOrNull
import net.corda.core.serialization.CordaSerializable
import rx.Observable import rx.Observable
/** /**
@ -19,6 +20,7 @@ import rx.Observable
*/ */
interface NetworkMapCache { interface NetworkMapCache {
@CordaSerializable
sealed class MapChange(val node: NodeInfo) { sealed class MapChange(val node: NodeInfo) {
class Added(node: NodeInfo) : MapChange(node) class Added(node: NodeInfo) : MapChange(node)
class Removed(node: NodeInfo) : MapChange(node) class Removed(node: NodeInfo) : MapChange(node)
@ -142,6 +144,7 @@ interface NetworkMapCache {
fun runWithoutMapService() fun runWithoutMapService()
} }
@CordaSerializable
sealed class NetworkCacheError : Exception() { sealed class NetworkCacheError : Exception() {
/** Indicates a failure to deregister, because of a rejected request from the remote node */ /** Indicates a failure to deregister, because of a rejected request from the remote node */
class DeregistrationFailed : NetworkCacheError() class DeregistrationFailed : NetworkCacheError()

View File

@ -1,5 +1,7 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.serialization.CordaSerializable
/** /**
* A container for additional information for an advertised service. * A container for additional information for an advertised service.
* *
@ -7,6 +9,7 @@ package net.corda.core.node.services
* @param name the service name, used for differentiating multiple services of the same type. Can also be used as a * @param name the service name, used for differentiating multiple services of the same type. Can also be used as a
* grouping identifier for nodes collectively running a distributed service. * grouping identifier for nodes collectively running a distributed service.
*/ */
@CordaSerializable
data class ServiceInfo(val type: ServiceType, val name: String? = null) { data class ServiceInfo(val type: ServiceType, val name: String? = null) {
companion object { companion object {
fun parse(encoded: String): ServiceInfo { fun parse(encoded: String): ServiceInfo {

View File

@ -1,10 +1,13 @@
package net.corda.core.node.services package net.corda.core.node.services
import net.corda.core.serialization.CordaSerializable
/** /**
* Identifier for service types a node can expose over the network to other peers. These types are placed into network * Identifier for service types a node can expose over the network to other peers. These types are placed into network
* map advertisements. Services that are purely local and are not providing functionality to other parts of the network * map advertisements. Services that are purely local and are not providing functionality to other parts of the network
* don't need a declared service type. * don't need a declared service type.
*/ */
@CordaSerializable
sealed class ServiceType(val id: String) { sealed class ServiceType(val id: String) {
init { init {
// Enforce: // Enforce:

View File

@ -3,6 +3,7 @@ package net.corda.core.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.* import net.corda.core.crypto.*
import net.corda.core.serialization.CordaSerializable
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
@ -36,6 +37,7 @@ val DEFAULT_SESSION_ID = 0L
* Active means they haven't been consumed yet (or we don't know about it). * Active means they haven't been consumed yet (or we don't know about it).
* Relevant means they contain at least one of our pubkeys. * Relevant means they contain at least one of our pubkeys.
*/ */
@CordaSerializable
class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) { class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
/** /**
@ -46,6 +48,7 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
* If the vault observes multiple transactions simultaneously, where some transactions consume the outputs of some of the * If the vault observes multiple transactions simultaneously, where some transactions consume the outputs of some of the
* other transactions observed, then the changes are observed "net" of those. * other transactions observed, then the changes are observed "net" of those.
*/ */
@CordaSerializable
data class Update(val consumed: Set<StateAndRef<ContractState>>, val produced: Set<StateAndRef<ContractState>>) { data class Update(val consumed: Set<StateAndRef<ContractState>>, val produced: Set<StateAndRef<ContractState>>) {
/** Checks whether the update contains a state of the specified type. */ /** Checks whether the update contains a state of the specified type. */
inline fun <reified T : ContractState> containsType() = consumed.any { it.state.data is T } || produced.any { it.state.data is T } inline fun <reified T : ContractState> containsType() = consumed.any { it.state.data is T } || produced.any { it.state.data is T }

View File

@ -2,8 +2,10 @@ package net.corda.core.node.services
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.serialization.CordaSerializable
import rx.Observable import rx.Observable
@CordaSerializable
data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRunId, val transactionId: SecureHash) data class StateMachineTransactionMapping(val stateMachineRunId: StateMachineRunId, val transactionId: SecureHash)
/** /**

View File

@ -3,6 +3,7 @@ package net.corda.core.node.services
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
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.serialization.CordaSerializable
/** /**
* A service that records input states of the given transaction and provides conflict information * A service that records input states of the given transaction and provides conflict information
@ -15,6 +16,7 @@ interface UniquenessProvider {
fun commit(states: List<StateRef>, txId: SecureHash, callerIdentity: Party) fun commit(states: List<StateRef>, txId: SecureHash, callerIdentity: Party)
/** Specifies the consuming transaction for every conflicting state */ /** Specifies the consuming transaction for every conflicting state */
@CordaSerializable
data class Conflict(val stateHistory: Map<StateRef, ConsumingTx>) data class Conflict(val stateHistory: Map<StateRef, ConsumingTx>)
/** /**
@ -26,7 +28,9 @@ interface UniquenessProvider {
* This allows a party to just submit invalid transactions with outputs it was aware of and * This allows a party to just submit invalid transactions with outputs it was aware of and
* find out where exactly they were spent. * find out where exactly they were spent.
*/ */
@CordaSerializable
data class ConsumingTx(val id: SecureHash, val inputIndex: Int, val requestingParty: Party) data class ConsumingTx(val id: SecureHash, val inputIndex: Int, val requestingParty: Party)
} }
@CordaSerializable
class UniquenessException(val error: UniquenessProvider.Conflict) : Exception() class UniquenessException(val error: UniquenessProvider.Conflict) : Exception()

View File

@ -9,6 +9,7 @@ import java.util.*
* In an ideal JVM this would be a value type and be completely overhead free. Project Valhalla is adding such * In an ideal JVM this would be a value type and be completely overhead free. Project Valhalla is adding such
* functionality to Java, but it won't arrive for a few years yet! * functionality to Java, but it won't arrive for a few years yet!
*/ */
@CordaSerializable
open class OpaqueBytes(val bytes: ByteArray) { open class OpaqueBytes(val bytes: ByteArray) {
init { init {
check(bytes.isNotEmpty()) check(bytes.isNotEmpty())

View File

@ -0,0 +1,178 @@
package net.corda.core.serialization
import com.esotericsoftware.kryo.*
import com.esotericsoftware.kryo.util.DefaultClassResolver
import com.esotericsoftware.kryo.util.Util
import net.corda.core.node.AttachmentsClassLoader
import net.corda.core.utilities.loggerFor
import java.io.PrintWriter
import java.lang.reflect.Modifier
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardOpenOption
import java.util.*
fun Kryo.addToWhitelist(type: Class<*>) {
((classResolver as? CordaClassResolver)?.whitelist as? MutableClassWhitelist)?.add(type)
}
fun makeStandardClassResolver(): ClassResolver {
return CordaClassResolver(GlobalTransientClassWhiteList(BuiltInExceptionsWhitelist()))
}
fun makeNoWhitelistClassResolver(): ClassResolver {
return CordaClassResolver(AllWhitelist)
}
class CordaClassResolver(val whitelist: ClassWhitelist) : DefaultClassResolver() {
companion object {
private val logger = loggerFor<CordaClassResolver>()
}
/** Returns the registration for the specified class, or null if the class is not registered. */
override fun getRegistration(type: Class<*>): Registration? {
return super.getRegistration(type) ?: checkClass(type)
}
private var whitelistEnabled = true
fun disableWhitelist() {
whitelistEnabled = false
}
fun enableWhitelist() {
whitelistEnabled = true
}
private fun checkClass(type: Class<*>): Registration? {
/** If call path has disabled whitelisting (see [CordaKryo.register]), just return without checking. */
if(!whitelistEnabled) return null
// Allow primitives, abstracts and interfaces
if (type.isPrimitive || type == Any::class.java || Modifier.isAbstract(type.modifiers) || type==String::class.java) return null
// If array, recurse on element type
if (type.isArray) {
return checkClass(type.componentType)
}
if (!type.isEnum && Enum::class.java.isAssignableFrom(type)) {
// Specialised enum entry, so just resolve the parent Enum type since cannot annotate the specialised entry.
return checkClass(type.superclass)
}
// It's safe to have the Class already, since Kryo loads it with initialisation off.
val hasAnnotation = checkForAnnotation(type)
if (!hasAnnotation && !whitelist.hasListed(type)) {
throw KryoException("Class ${Util.className(type)} is not annotated or on the whitelist, so cannot be used in serialization")
}
return null
}
override fun registerImplicit(type: Class<*>): Registration {
// We have to set reference to true, since the flag influences how String fields are treated and we want it to be consistent.
val references = kryo.references
try {
kryo.references = true
return register(Registration(type, kryo.getDefaultSerializer(type), NAME.toInt()))
} finally {
kryo.references = references
}
}
// We don't allow the annotation for classes in attachments for now. The class will be on the main classpath if we have the CorDapp installed.
// We also do not allow extension of KryoSerializable for annotated classes, or combination with @DefaultSerializer for custom serialisation.
// TODO: Later we can support annotations on attachment classes and spin up a proxy via bytecode that we know is harmless.
private fun checkForAnnotation(type: Class<*>): Boolean {
return (type.classLoader !is AttachmentsClassLoader)
&& !KryoSerializable::class.java.isAssignableFrom(type)
&& !type.isAnnotationPresent(DefaultSerializer::class.java)
&& (type.isAnnotationPresent(CordaSerializable::class.java) || hasAnnotationOnInterface(type))
}
// Recursively check interfaces for our annotation.
private fun hasAnnotationOnInterface(type: Class<*>): Boolean {
return type.interfaces.any { it.isAnnotationPresent(CordaSerializable::class.java) || hasAnnotationOnInterface(it) }
|| (type.superclass != null && hasAnnotationOnInterface(type.superclass))
}
}
interface ClassWhitelist {
fun hasListed(type: Class<*>): Boolean
}
interface MutableClassWhitelist : ClassWhitelist {
fun add(entry: Class<*>)
}
object EmptyWhitelist : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = false
}
class BuiltInExceptionsWhitelist : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = Throwable::class.java.isAssignableFrom(type) && type.`package`.name.startsWith("java.")
}
object AllWhitelist : ClassWhitelist {
override fun hasListed(type: Class<*>): Boolean = true
}
// TODO: Need some concept of from which class loader
class GlobalTransientClassWhiteList(val delegate: ClassWhitelist) : MutableClassWhitelist, ClassWhitelist by delegate {
companion object {
val whitelist: MutableSet<String> = Collections.synchronizedSet(mutableSetOf())
}
override fun hasListed(type: Class<*>): Boolean {
return (type.name in whitelist) || delegate.hasListed(type)
}
override fun add(entry: Class<*>) {
whitelist += entry.name
}
}
/**
* This class is not currently used, but can be installed to log a large number of missing entries from the whitelist
* and was used to track down the initial set.
*/
class LoggingWhitelist(val delegate: ClassWhitelist, val global: Boolean = true) : MutableClassWhitelist {
companion object {
val log = loggerFor<LoggingWhitelist>()
val globallySeen: MutableSet<String> = Collections.synchronizedSet(mutableSetOf())
val journalWriter: PrintWriter? = openOptionalDynamicWhitelistJournal()
private fun openOptionalDynamicWhitelistJournal(): PrintWriter? {
val fileName = System.getenv("WHITELIST_FILE")
if (fileName != null && fileName.isNotEmpty()) {
try {
return PrintWriter(Files.newBufferedWriter(Paths.get(fileName), StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND, StandardOpenOption.WRITE), true)
} catch(ioEx: Exception) {
log.error("Could not open/create whitelist journal file for append: $fileName", ioEx)
}
}
return null
}
}
private val locallySeen: MutableSet<String> = mutableSetOf()
private val alreadySeen: MutableSet<String> get() = if (global) globallySeen else locallySeen
override fun hasListed(type: Class<*>): Boolean {
if (type.name !in alreadySeen && !delegate.hasListed(type)) {
alreadySeen += type.name
val className = Util.className(type)
log.warn("Dynamically whitelisted class $className")
if (journalWriter != null) {
journalWriter.println(className)
}
}
return true
}
override fun add(entry: Class<*>) {
if (delegate is MutableClassWhitelist) {
delegate.add(entry)
} else {
throw UnsupportedOperationException("Cannot add to whitelist since delegate whitelist is not mutable.")
}
}
}

View File

@ -0,0 +1,19 @@
package net.corda.core.serialization
import java.lang.annotation.Inherited
/**
* This annotation is a marker to indicate that a class is permitted and intended to be serialized as part of Node messaging.
*
* Strictly speaking, it is critical to identifying that a class is intended to be deserialized by the node, to avoid
* a security compromise later when a vulnerability is discovered in the deserialisation of a class that just happens to
* be on the classpath, perhaps from a 3rd party library, as has been witnessed elsewhere.
*
* It also makes it possible for a code reviewer to clearly identify the classes that can be passed on the wire.
*
* TODO: As we approach a long term wire format, this annotation will only be permitted on classes that meet certain criteria.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Inherited
annotation class CordaSerializable

View File

@ -0,0 +1,74 @@
package net.corda.core.serialization
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.util.MapReferenceResolver
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.core.crypto.CompositeKey
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.NonEmptySet
import net.corda.core.utilities.NonEmptySetSerializer
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.objenesis.strategy.StdInstantiatorStrategy
import java.io.BufferedInputStream
import java.util.*
object DefaultKryoCustomizer {
private val pluginRegistries: List<CordaPluginRegistry> by lazy {
// No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
val unusedKryo = Kryo(makeStandardClassResolver(), MapReferenceResolver())
val customization = KryoSerializationCustomization(unusedKryo)
ServiceLoader.load(CordaPluginRegistry::class.java).toList().filter { it.customizeSerialization(customization) }
}
// TODO: move all register() to addDefaultSerializer()
fun customize(kryo: Kryo): Kryo {
return kryo.apply {
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
// no-arg constructor available.
instantiatorStrategy = Kryo.DefaultInstantiatorStrategy(StdInstantiatorStrategy())
register(Arrays.asList("").javaClass, ArraysAsListSerializer())
register(SignedTransaction::class.java, ImmutableClassSerializer(SignedTransaction::class))
register(WireTransaction::class.java, WireTransactionSerializer)
register(SerializedBytes::class.java, SerializedBytesSerializer)
UnmodifiableCollectionsSerializer.registerSerializers(this)
ImmutableListSerializer.registerSerializers(this)
ImmutableSetSerializer.registerSerializers(this)
ImmutableSortedSetSerializer.registerSerializers(this)
ImmutableMapSerializer.registerSerializers(this)
ImmutableMultimapSerializer.registerSerializers(this)
register(BufferedInputStream::class.java, InputStreamSerializer)
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer)
noReferencesWithin<WireTransaction>()
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
// Using a custom serializer for compactness
register(CompositeKey.Node::class.java, CompositeKeyNodeSerializer)
register(CompositeKey.Leaf::class.java, CompositeKeyLeafSerializer)
// Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway.
register(Array<StackTraceElement>::class, read = { kryo, input -> emptyArray() }, write = { kryo, output, obj -> })
// This ensures a NonEmptySetSerializer is constructed with an initial value.
register(NonEmptySet::class.java, NonEmptySetSerializer)
/** This ensures any kotlin objects that implement [DeserializeAsKotlinObjectDef] are read back in as singletons. */
addDefaultSerializer(DeserializeAsKotlinObjectDef::class.java, KotlinObjectSerializer)
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
val customization = KryoSerializationCustomization(this)
pluginRegistries.forEach { it.customizeSerialization(customization) }
}
}
}

View File

@ -1,38 +1,25 @@
package net.corda.core.serialization package net.corda.core.serialization
import co.paralleluniverse.fibers.Fiber import com.esotericsoftware.kryo.*
import co.paralleluniverse.io.serialization.kryo.KryoSerializer
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.Kryo.DefaultInstantiatorStrategy
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Registration
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input 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 com.esotericsoftware.kryo.util.MapReferenceResolver
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
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
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
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.NonEmptySetSerializer
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
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.objenesis.strategy.StdInstantiatorStrategy
import java.io.* import java.io.*
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.security.PublicKey
import java.time.Instant
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import kotlin.reflect.* import kotlin.reflect.*
@ -64,16 +51,25 @@ import kotlin.reflect.jvm.javaType
* in invalid states, thus violating system invariants. It isn't designed to handle malicious streams and therefore, * in invalid states, thus violating system invariants. It isn't designed to handle malicious streams and therefore,
* isn't usable beyond the prototyping stage. But that's fine: we can revisit serialisation technologies later after * isn't usable beyond the prototyping stage. But that's fine: we can revisit serialisation technologies later after
* a formal evaluation process. * a formal evaluation process.
*
* We now distinguish between internal, storage related Kryo and external, network facing Kryo. We presently use
* some non-whitelisted classes as part of internal storage.
* TODO: eliminate internal, storage related whitelist issues, such as private keys in blob storage.
*/ */
// A convenient instance of Kryo pre-configured with some useful things. Used as a default by various functions. // A convenient instance of Kryo pre-configured with some useful things. Used as a default by various functions.
val THREAD_LOCAL_KRYO: ThreadLocal<Kryo> = ThreadLocal.withInitial { createKryo() } private val THREAD_LOCAL_KRYO: ThreadLocal<Kryo> = ThreadLocal.withInitial { createKryo() }
// Same again, but this has whitelisting turned off for internal storage use only.
private val INTERNAL_THREAD_LOCAL_KRYO: ThreadLocal<Kryo> = ThreadLocal.withInitial { createInternalKryo() }
fun threadLocalP2PKryo(): Kryo = THREAD_LOCAL_KRYO.get()
fun threadLocalStorageKryo(): Kryo = INTERNAL_THREAD_LOCAL_KRYO.get()
/** /**
* A type safe wrapper around a byte array that contains a serialised object. You can call [SerializedBytes.deserialize] * A type safe wrapper around a byte array that contains a serialised object. You can call [SerializedBytes.deserialize]
* to get the original object back. * to get the original object back.
*/ */
class SerializedBytes<T : Any>(bytes: ByteArray) : OpaqueBytes(bytes) { class SerializedBytes<T : Any>(bytes: ByteArray, val internalOnly: Boolean = false) : OpaqueBytes(bytes) {
// It's OK to use lazy here because SerializedBytes is configured to use the ImmutableClassSerializer. // It's OK to use lazy here because SerializedBytes is configured to use the ImmutableClassSerializer.
val hash: SecureHash by lazy { bytes.sha256() } val hash: SecureHash by lazy { bytes.sha256() }
@ -81,20 +77,20 @@ class SerializedBytes<T : Any>(bytes: ByteArray) : OpaqueBytes(bytes) {
} }
// Some extension functions that make deserialisation convenient and provide auto-casting of the result. // Some extension functions that make deserialisation convenient and provide auto-casting of the result.
fun <T : Any> ByteArray.deserialize(kryo: Kryo = THREAD_LOCAL_KRYO.get()): T { fun <T : Any> ByteArray.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return kryo.readClassAndObject(Input(this)) as T return kryo.readClassAndObject(Input(this)) as T
} }
fun <T : Any> OpaqueBytes.deserialize(kryo: Kryo = THREAD_LOCAL_KRYO.get()): T { fun <T : Any> OpaqueBytes.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
return this.bytes.deserialize(kryo) return this.bytes.deserialize(kryo)
} }
// The more specific deserialize version results in the bytes being cached, which is faster. // The more specific deserialize version results in the bytes being cached, which is faster.
@JvmName("SerializedBytesWireTransaction") @JvmName("SerializedBytesWireTransaction")
fun SerializedBytes<WireTransaction>.deserialize(kryo: Kryo = THREAD_LOCAL_KRYO.get()): WireTransaction = WireTransaction.deserialize(this, kryo) fun SerializedBytes<WireTransaction>.deserialize(kryo: Kryo = threadLocalP2PKryo()): WireTransaction = WireTransaction.deserialize(this, kryo)
fun <T : Any> SerializedBytes<T>.deserialize(kryo: Kryo = THREAD_LOCAL_KRYO.get()): T = bytes.deserialize(kryo) fun <T : Any> SerializedBytes<T>.deserialize(kryo: Kryo = if (internalOnly) threadLocalStorageKryo() else threadLocalP2PKryo()): T = bytes.deserialize(kryo)
/** /**
* A serialiser that avoids writing the wrapper class to the byte stream, thus ensuring [SerializedBytes] is a pure * A serialiser that avoids writing the wrapper class to the byte stream, thus ensuring [SerializedBytes] is a pure
@ -115,12 +111,12 @@ object SerializedBytesSerializer : Serializer<SerializedBytes<Any>>() {
* Can be called on any object to convert it to a byte array (wrapped by [SerializedBytes]), regardless of whether * Can be called on any object to convert it to a byte array (wrapped by [SerializedBytes]), regardless of whether
* the type is marked as serializable or was designed for it (so be careful!). * the type is marked as serializable or was designed for it (so be careful!).
*/ */
fun <T : Any> T.serialize(kryo: Kryo = THREAD_LOCAL_KRYO.get()): SerializedBytes<T> { fun <T : Any> T.serialize(kryo: Kryo = threadLocalP2PKryo(), internalOnly: Boolean = false): SerializedBytes<T> {
val stream = ByteArrayOutputStream() val stream = ByteArrayOutputStream()
Output(stream).use { Output(stream).use {
kryo.writeClassAndObject(it, this) kryo.writeClassAndObject(it, this)
} }
return SerializedBytes(stream.toByteArray()) return SerializedBytes(stream.toByteArray(), internalOnly)
} }
/** /**
@ -261,6 +257,7 @@ fun Input.readBytesWithLength(): ByteArray {
} }
/** Thrown during deserialisation to indicate that an attachment needed to construct the [WireTransaction] is not found */ /** Thrown during deserialisation to indicate that an attachment needed to construct the [WireTransaction] is not found */
@CordaSerializable
class MissingAttachmentsException(val ids: List<SecureHash>) : Exception() class MissingAttachmentsException(val ids: List<SecureHash>) : Exception()
/** A serialisation engine that knows how to deserialise code inside a sandbox */ /** A serialisation engine that knows how to deserialise code inside a sandbox */
@ -389,66 +386,55 @@ object KotlinObjectSerializer : Serializer<DeserializeAsKotlinObjectDef>() {
override fun write(kryo: Kryo, output: Output, obj: DeserializeAsKotlinObjectDef) {} override fun write(kryo: Kryo, output: Output, obj: DeserializeAsKotlinObjectDef) {}
} }
fun createKryo(k: Kryo = Kryo()): Kryo { // No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
return k.apply { fun createInternalKryo(k: Kryo = CordaKryo(makeNoWhitelistClassResolver())): Kryo {
// Allow any class to be deserialized (this is insecure but for prototyping we don't care) return DefaultKryoCustomizer.customize(k)
isRegistrationRequired = false }
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
// no-arg constructor available.
instantiatorStrategy = DefaultInstantiatorStrategy(StdInstantiatorStrategy())
register(Arrays.asList("").javaClass, ArraysAsListSerializer()) // No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
fun createKryo(k: Kryo = CordaKryo(makeStandardClassResolver())): Kryo {
return DefaultKryoCustomizer.customize(k)
}
// Because we like to stick a Kryo object in a ThreadLocal to speed things up a bit, we can end up trying to /**
// serialise the Kryo object itself when suspending a fiber. That's dumb, useless AND can cause crashes, so * We need to disable whitelist checking during calls from our Kryo code to register a serializer, since it checks
// we avoid it here. * for existing registrations and then will enter our [CordaClassResolver.getRegistration] method.
register(Kryo::class, */
read = { kryo, input -> createKryo((Fiber.getFiberSerializer() as KryoSerializer).kryo) }, open class CordaKryo(classResolver: ClassResolver) : Kryo(classResolver, MapReferenceResolver()) {
write = { kryo, output, obj -> } override fun register(type: Class<*>?): Registration {
) (classResolver as? CordaClassResolver)?.disableWhitelist()
try {
return super.register(type)
} finally {
(classResolver as? CordaClassResolver)?.enableWhitelist()
}
}
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer) override fun register(type: Class<*>?, id: Int): Registration {
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer) (classResolver as? CordaClassResolver)?.disableWhitelist()
register(Instant::class.java, ReferencesAwareJavaSerializer) try {
return super.register(type, id)
} finally {
(classResolver as? CordaClassResolver)?.enableWhitelist()
}
}
// Using a custom serializer for compactness override fun register(type: Class<*>?, serializer: Serializer<*>?): Registration {
register(CompositeKey.Node::class.java, CompositeKeyNodeSerializer) (classResolver as? CordaClassResolver)?.disableWhitelist()
register(CompositeKey.Leaf::class.java, CompositeKeyLeafSerializer) try {
return super.register(type, serializer)
} finally {
(classResolver as? CordaClassResolver)?.enableWhitelist()
}
}
// Some classes have to be handled with the ImmutableClassSerializer because they need to have their override fun register(registration: Registration?): Registration {
// constructors be invoked (typically for lazy members). (classResolver as? CordaClassResolver)?.disableWhitelist()
register(SignedTransaction::class.java, ImmutableClassSerializer(SignedTransaction::class)) try {
return super.register(registration)
// This class has special handling. } finally {
register(WireTransaction::class.java, WireTransactionSerializer) (classResolver as? CordaClassResolver)?.enableWhitelist()
}
// This ensures a SerializedBytes<Foo> wrapper is written out as just a byte array.
register(SerializedBytes::class.java, SerializedBytesSerializer)
addDefaultSerializer(SerializeAsToken::class.java, SerializeAsTokenSerializer<SerializeAsToken>())
// This is required to make all the unit tests pass
register(AnonymousParty::class.java)
register(Party::class.java)
// This ensures a NonEmptySetSerializer is constructed with an initial value.
register(NonEmptySet::class.java, NonEmptySetSerializer)
register(Array<StackTraceElement>::class, read = { kryo, input -> emptyArray() }, write = { kryo, output, o -> })
/** This ensures any kotlin objects that implement [DeserializeAsKotlinObjectDef] are read back in as singletons. */
addDefaultSerializer(DeserializeAsKotlinObjectDef::class.java, KotlinObjectSerializer)
addDefaultSerializer(BufferedInputStream::class.java, InputStreamSerializer)
UnmodifiableCollectionsSerializer.registerSerializers(k)
ImmutableListSerializer.registerSerializers(k)
ImmutableSetSerializer.registerSerializers(k)
ImmutableSortedSetSerializer.registerSerializers(k)
ImmutableMapSerializer.registerSerializers(k)
ImmutableMultimapSerializer.registerSerializers(k)
noReferencesWithin<WireTransaction>()
} }
} }

View File

@ -0,0 +1,13 @@
package net.corda.core.serialization
import com.esotericsoftware.kryo.Kryo
interface SerializationCustomization {
fun addToWhitelist(type: Class<*>)
}
class KryoSerializationCustomization(val kryo: Kryo) : SerializationCustomization {
override fun addToWhitelist(type: Class<*>) {
kryo.addToWhitelist(type)
}
}

View File

@ -22,6 +22,7 @@ import java.util.*
* *
* This models a similar pattern to the readReplace/writeReplace methods in Java serialization. * This models a similar pattern to the readReplace/writeReplace methods in Java serialization.
*/ */
@CordaSerializable
interface SerializeAsToken { interface SerializeAsToken {
fun toToken(context: SerializeAsTokenContext): SerializationToken fun toToken(context: SerializeAsTokenContext): SerializationToken
} }
@ -100,6 +101,7 @@ class SerializeAsTokenContext(toBeTokenized: Any, kryo: Kryo = createKryo()) {
* A class representing a [SerializationToken] for some object that is not serializable but can be looked up * A class representing a [SerializationToken] for some object that is not serializable but can be looked up
* (when deserialized) via just the class name. * (when deserialized) via just the class name.
*/ */
@CordaSerializable
data class SingletonSerializationToken private constructor(private val className: String) : SerializationToken { data class SingletonSerializationToken private constructor(private val className: String) : SerializationToken {
constructor(toBeTokenized: SerializeAsToken) : this(toBeTokenized.javaClass.name) constructor(toBeTokenized: SerializeAsToken) : this(toBeTokenized.javaClass.name)

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.*
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
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.serialization.CordaSerializable
/** /**
* 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:
@ -16,6 +17,7 @@ import net.corda.core.crypto.SecureHash
* *
* All the above refer to inputs using a (txhash, output index) pair. * All the above refer to inputs using a (txhash, output index) pair.
*/ */
@CordaSerializable
class LedgerTransaction( class LedgerTransaction(
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */ /** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
override val inputs: List<StateAndRef<*>>, override val inputs: List<StateAndRef<*>>,

View File

@ -2,6 +2,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.CordaSerializable
import net.corda.core.serialization.createKryo import net.corda.core.serialization.createKryo
import net.corda.core.serialization.extendKryoHash import net.corda.core.serialization.extendKryoHash
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -67,6 +68,7 @@ interface TraversableTransaction {
* Class that holds filtered leaves for a partial Merkle transaction. We assume mixed leaf types, notice that every * Class that holds filtered leaves for a partial Merkle transaction. We assume mixed leaf types, notice that every
* field from [WireTransaction] can be used in [PartialMerkleTree] calculation. * field from [WireTransaction] can be used in [PartialMerkleTree] calculation.
*/ */
@CordaSerializable
class FilteredLeaves( class FilteredLeaves(
override val inputs: List<StateRef>, override val inputs: List<StateRef>,
override val attachments: List<SecureHash>, override val attachments: List<SecureHash>,
@ -98,6 +100,7 @@ class FilteredLeaves(
* @param filteredLeaves Leaves included in a filtered transaction. * @param filteredLeaves Leaves included in a filtered transaction.
* @param partialMerkleTree Merkle branch needed to verify filteredLeaves. * @param partialMerkleTree Merkle branch needed to verify filteredLeaves.
*/ */
@CordaSerializable
class FilteredTransaction private constructor( class FilteredTransaction private constructor(
val rootHash: SecureHash, val rootHash: SecureHash,
val filteredLeaves: FilteredLeaves, val filteredLeaves: FilteredLeaves,

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.DigitalSignature
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.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import java.security.KeyPair import java.security.KeyPair
import java.security.SignatureException import java.security.SignatureException
@ -41,6 +42,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
*/ */
override val id: SecureHash get() = tx.id override val id: SecureHash get() = tx.id
@CordaSerializable
class SignaturesMissingException(val missing: Set<CompositeKey>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() { class SignaturesMissingException(val missing: Set<CompositeKey>, 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.joinToString()}" return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}"

View File

@ -9,9 +9,9 @@ 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
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.THREAD_LOCAL_KRYO
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.serialization.threadLocalP2PKryo
import net.corda.core.utilities.Emoji import net.corda.core.utilities.Emoji
import java.security.PublicKey import java.security.PublicKey
@ -45,7 +45,7 @@ class WireTransaction(
override val id: SecureHash by lazy { merkleTree.hash } override val id: SecureHash by lazy { merkleTree.hash }
companion object { companion object {
fun deserialize(data: SerializedBytes<WireTransaction>, kryo: Kryo = THREAD_LOCAL_KRYO.get()): WireTransaction { fun deserialize(data: SerializedBytes<WireTransaction>, kryo: Kryo = threadLocalP2PKryo()): WireTransaction {
val wtx = data.bytes.deserialize<WireTransaction>(kryo) val wtx = data.bytes.deserialize<WireTransaction>(kryo)
wtx.cachedBytes = data wtx.cachedBytes = data
return wtx return wtx

View File

@ -1,6 +1,7 @@
package net.corda.core.utilities package net.corda.core.utilities
import net.corda.core.TransientProperty import net.corda.core.TransientProperty
import net.corda.core.serialization.CordaSerializable
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
import rx.subjects.BehaviorSubject import rx.subjects.BehaviorSubject
@ -32,7 +33,9 @@ import java.util.*
* A progress tracker is *not* thread safe. You may move events from the thread making progress to another thread by * A progress tracker is *not* thread safe. You may move events from the thread making progress to another thread by
* using the [Observable] subscribeOn call. * using the [Observable] subscribeOn call.
*/ */
@CordaSerializable
class ProgressTracker(vararg steps: Step) { class ProgressTracker(vararg steps: Step) {
@CordaSerializable
sealed class Change { sealed class Change {
class Position(val tracker: ProgressTracker, val newStep: Step) : Change() { class Position(val tracker: ProgressTracker, val newStep: Step) : Change() {
override fun toString() = newStep.label override fun toString() = newStep.label
@ -48,6 +51,7 @@ class ProgressTracker(vararg steps: Step) {
} }
/** The superclass of all step objects. */ /** The superclass of all step objects. */
@CordaSerializable
open class Step(open val label: String) { open class Step(open val label: String) {
open val changes: Observable<Change> get() = Observable.empty() open val changes: Observable<Change> get() = Observable.empty()
open fun childProgressTracker(): ProgressTracker? = null open fun childProgressTracker(): ProgressTracker? = null
@ -81,6 +85,7 @@ class ProgressTracker(vararg steps: Step) {
// This field won't be serialized. // This field won't be serialized.
private val _changes by TransientProperty { PublishSubject.create<Change>() } private val _changes by TransientProperty { PublishSubject.create<Change>() }
@CordaSerializable
private data class Child(val tracker: ProgressTracker, @Transient val subscription: Subscription?) private data class Child(val tracker: ProgressTracker, @Transient val subscription: Subscription?)
private val childProgressTrackers = HashMap<Step, Child>() private val childProgressTrackers = HashMap<Step, Child>()

View File

@ -11,6 +11,7 @@ import net.corda.core.crypto.signWithECDSA
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.recordTransactions import net.corda.core.node.recordTransactions
import net.corda.core.serialization.CordaSerializable
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
@ -29,6 +30,7 @@ abstract class AbstractStateReplacementFlow {
* *
* @param M the type of a class representing proposed modification by the instigator. * @param M the type of a class representing proposed modification by the instigator.
*/ */
@CordaSerializable
data class Proposal<out M>(val stateRef: StateRef, val modification: M, val stx: SignedTransaction) data class Proposal<out M>(val stateRef: StateRef, val modification: M, val stx: SignedTransaction)
/** /**

View File

@ -3,6 +3,7 @@ package net.corda.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -17,6 +18,7 @@ import net.corda.core.transactions.SignedTransaction
*/ */
class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction, class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction,
val participants: Set<Party>) : FlowLogic<Unit>() { val participants: Set<Party>) : FlowLogic<Unit>() {
@CordaSerializable
data class NotifyTxRequest(val tx: SignedTransaction) data class NotifyTxRequest(val tx: SignedTransaction)
@Suspendable @Suspendable

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.flows.FetchDataFlow.DownloadedVsRequestedDataMismatch import net.corda.flows.FetchDataFlow.DownloadedVsRequestedDataMismatch
@ -32,11 +33,18 @@ abstract class FetchDataFlow<T : NamedByHash, in W : Any>(
protected val requests: Set<SecureHash>, protected val requests: Set<SecureHash>,
protected val otherSide: Party) : FlowLogic<FetchDataFlow.Result<T>>() { protected val otherSide: Party) : FlowLogic<FetchDataFlow.Result<T>>() {
@CordaSerializable
class DownloadedVsRequestedDataMismatch(val requested: SecureHash, val got: SecureHash) : IllegalArgumentException() class DownloadedVsRequestedDataMismatch(val requested: SecureHash, val got: SecureHash) : IllegalArgumentException()
@CordaSerializable
class DownloadedVsRequestedSizeMismatch(val requested: Int, val got: Int) : IllegalArgumentException() class DownloadedVsRequestedSizeMismatch(val requested: Int, val got: Int) : IllegalArgumentException()
class HashNotFound(val requested: SecureHash) : FlowException() class HashNotFound(val requested: SecureHash) : FlowException()
@CordaSerializable
data class Request(val hashes: List<SecureHash>) data class Request(val hashes: List<SecureHash>)
@CordaSerializable
data class Result<out T : NamedByHash>(val fromDisk: List<T>, val downloaded: List<T>) data class Result<out T : NamedByHash>(val fromDisk: List<T>, val downloaded: List<T>)
@Suspendable @Suspendable

View File

@ -9,6 +9,7 @@ import net.corda.core.flows.FlowLogic
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.serialization.CordaSerializable
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.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
@ -161,6 +162,7 @@ class NotaryException(val error: NotaryError) : FlowException() {
override fun toString() = "${super.toString()}: Error response from Notary - $error" override fun toString() = "${super.toString()}: Error response from Notary - $error"
} }
@CordaSerializable
sealed class NotaryError { sealed class NotaryError {
class Conflict(val txId: SecureHash, val conflict: SignedData<UniquenessProvider.Conflict>) : NotaryError() { class Conflict(val txId: SecureHash, val conflict: SignedData<UniquenessProvider.Conflict>) : NotaryError() {
override fun toString() = "One or more input states for transaction $txId have been used in another transaction" override fun toString() = "One or more input states for transaction $txId have been used in another transaction"

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.recordTransactions import net.corda.core.node.recordTransactions
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
@ -67,6 +68,7 @@ class ResolveTransactionsFlow(private val txHashes: Set<SecureHash>,
} }
@CordaSerializable
class ExcessivelyLargeTransactionGraph() : Exception() class ExcessivelyLargeTransactionGraph() : Exception()
// Transactions to verify after the dependencies. // Transactions to verify after the dependencies.

View File

@ -3,10 +3,12 @@ package net.corda.flows
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.messaging.* import net.corda.core.messaging.*
import net.corda.core.node.services.DEFAULT_SESSION_ID import net.corda.core.node.services.DEFAULT_SESSION_ID
import net.corda.core.serialization.CordaSerializable
/** /**
* Abstract superclass for request messages sent to services which expect a reply. * Abstract superclass for request messages sent to services which expect a reply.
*/ */
@CordaSerializable
interface ServiceRequestMessage { interface ServiceRequestMessage {
val sessionID: Long val sessionID: Long
val replyTo: SingleMessageRecipient val replyTo: SingleMessageRecipient

View File

@ -10,6 +10,7 @@ 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
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction 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
@ -31,17 +32,21 @@ import java.security.KeyPair
*/ */
object TwoPartyDealFlow { object TwoPartyDealFlow {
@CordaSerializable
class DealMismatchException(val expectedDeal: ContractState, val actualDeal: ContractState) : Exception() { class DealMismatchException(val expectedDeal: ContractState, val actualDeal: ContractState) : Exception() {
override fun toString() = "The submitted deal didn't match the expected: $expectedDeal vs $actualDeal" override fun toString() = "The submitted deal didn't match the expected: $expectedDeal vs $actualDeal"
} }
@CordaSerializable
class DealRefMismatchException(val expectedDeal: StateRef, val actualDeal: StateRef) : Exception() { class DealRefMismatchException(val expectedDeal: StateRef, val actualDeal: StateRef) : Exception() {
override fun toString() = "The submitted deal didn't match the expected: $expectedDeal vs $actualDeal" override fun toString() = "The submitted deal didn't match the expected: $expectedDeal vs $actualDeal"
} }
// This object is serialised to the network and is the first flow message the seller sends to the buyer. // This object is serialised to the network and is the first flow message the seller sends to the buyer.
@CordaSerializable
data class Handshake<out T>(val payload: T, val publicKey: CompositeKey) data class Handshake<out T>(val payload: T, val publicKey: CompositeKey)
@CordaSerializable
class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySigs: List<DigitalSignature.WithKey>) class SignaturesFromPrimary(val sellerSig: DigitalSignature.WithKey, val notarySigs: List<DigitalSignature.WithKey>)
/** /**
@ -263,7 +268,7 @@ object TwoPartyDealFlow {
@Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<CompositeKey>> @Suspendable protected abstract fun assembleSharedTX(handshake: Handshake<U>): Pair<TransactionBuilder, List<CompositeKey>>
} }
@CordaSerializable
data class AutoOffer(val notary: Party, val dealBeingOffered: DealState) data class AutoOffer(val notary: Party, val dealBeingOffered: DealState)

View File

@ -207,6 +207,7 @@ class AttachmentClassLoaderTests {
val kryo = createKryo() val kryo = createKryo()
kryo.classLoader = cl kryo.classLoader = cl
kryo.addToWhitelist(contract.javaClass)
val state2 = bytes.deserialize(kryo) val state2 = bytes.deserialize(kryo)
assert(state2.javaClass.classLoader is AttachmentsClassLoader) assert(state2.javaClass.classLoader is AttachmentsClassLoader)
@ -214,6 +215,7 @@ class AttachmentClassLoaderTests {
} }
// top level wrapper // top level wrapper
@CordaSerializable
class Data(val contract: Contract) class Data(val contract: Contract)
@Test @Test
@ -222,7 +224,9 @@ class AttachmentClassLoaderTests {
assertNotNull(data.contract) assertNotNull(data.contract)
val bytes = data.serialize() val kryo2 = createKryo()
kryo2.addToWhitelist(data.contract.javaClass)
val bytes = data.serialize(kryo2)
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
@ -234,6 +238,7 @@ class AttachmentClassLoaderTests {
val kryo = createKryo() val kryo = createKryo()
kryo.classLoader = cl kryo.classLoader = cl
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl))
val state2 = bytes.deserialize(kryo) val state2 = bytes.deserialize(kryo)
assertEquals(cl, state2.contract.javaClass.classLoader) assertEquals(cl, state2.contract.javaClass.classLoader)
@ -259,6 +264,9 @@ class AttachmentClassLoaderTests {
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY) val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val storage = MockAttachmentStorage() val storage = MockAttachmentStorage()
val kryo = createKryo() val kryo = createKryo()
kryo.addToWhitelist(contract.javaClass)
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$State", true, child))
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract\$Commands\$Create", true, child))
// todo - think about better way to push attachmentStorage down to serializer // todo - think about better way to push attachmentStorage down to serializer
kryo.attachmentStorage = storage kryo.attachmentStorage = storage

View File

@ -0,0 +1,161 @@
package net.corda.core.serialization
import com.esotericsoftware.kryo.*
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.util.MapReferenceResolver
import net.corda.core.node.AttachmentClassLoaderTests
import net.corda.core.node.AttachmentsClassLoader
import net.corda.core.node.services.AttachmentStorage
import net.corda.testing.node.MockAttachmentStorage
import org.junit.Test
@CordaSerializable
enum class Foo {
Bar {
override val value = 0
},
Stick {
override val value = 1
};
abstract val value: Int
}
@CordaSerializable
open class Element
open class SubElement : Element()
class SubSubElement : SubElement()
abstract class AbstractClass
interface Interface
@CordaSerializable
interface SerializableInterface
interface SerializableSubInterface : SerializableInterface
class NotSerializable
class SerializableViaInterface : SerializableInterface
open class SerializableViaSubInterface : SerializableSubInterface
class SerializableViaSuperSubInterface : SerializableViaSubInterface()
@CordaSerializable
class CustomSerializable : KryoSerializable {
override fun read(kryo: Kryo?, input: Input?) {
}
override fun write(kryo: Kryo?, output: Output?) {
}
}
@CordaSerializable
@DefaultSerializer(DefaultSerializableSerializer::class)
class DefaultSerializable
class DefaultSerializableSerializer : Serializer<DefaultSerializable>() {
override fun write(kryo: Kryo, output: Output, obj: DefaultSerializable) {
}
override fun read(kryo: Kryo, input: Input, type: Class<DefaultSerializable>): DefaultSerializable {
return DefaultSerializable()
}
}
class CordaClassResolverTests {
@Test
fun `Annotation on enum works for specialised entries`() {
CordaClassResolver(EmptyWhitelist).getRegistration(Foo.Bar::class.java)
}
@Test
fun `Annotation on array element works`() {
val values = arrayOf(Element())
CordaClassResolver(EmptyWhitelist).getRegistration(values.javaClass)
}
@Test
fun `Annotation not needed on abstract class`() {
CordaClassResolver(EmptyWhitelist).getRegistration(AbstractClass::class.java)
}
@Test
fun `Annotation not needed on interface`() {
CordaClassResolver(EmptyWhitelist).getRegistration(Interface::class.java)
}
@Test
fun `Calling register method on modified Kryo does not consult the whitelist`() {
val kryo = CordaKryo(CordaClassResolver(EmptyWhitelist))
kryo.register(NotSerializable::class.java)
}
@Test(expected = KryoException::class)
fun `Calling register method on unmodified Kryo does consult the whitelist`() {
val kryo = Kryo(CordaClassResolver(EmptyWhitelist), MapReferenceResolver())
kryo.register(NotSerializable::class.java)
}
@Test(expected = KryoException::class)
fun `Annotation is needed without whitelisting`() {
CordaClassResolver(EmptyWhitelist).getRegistration(NotSerializable::class.java)
}
@Test
fun `Annotation is not needed with whitelisting`() {
val resolver = CordaClassResolver(GlobalTransientClassWhiteList(EmptyWhitelist))
(resolver.whitelist as MutableClassWhitelist).add(NotSerializable::class.java)
resolver.getRegistration(NotSerializable::class.java)
}
@Test
fun `Annotation not needed on Object`() {
CordaClassResolver(EmptyWhitelist).getRegistration(Object::class.java)
}
@Test
fun `Annotation not needed on primitive`() {
CordaClassResolver(EmptyWhitelist).getRegistration(Integer.TYPE)
}
@Test(expected = KryoException::class)
fun `Annotation does not work for custom serializable`() {
CordaClassResolver(EmptyWhitelist).getRegistration(CustomSerializable::class.java)
}
@Test(expected = KryoException::class)
fun `Annotation does not work in conjunction with Kryo annotation`() {
CordaClassResolver(EmptyWhitelist).getRegistration(DefaultSerializable::class.java)
}
private fun importJar(storage: AttachmentStorage) = AttachmentClassLoaderTests.ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) }
@Test(expected = KryoException::class)
fun `Annotation does not work in conjunction with AttachmentClassLoader annotation`() {
val storage = MockAttachmentStorage()
val attachmentHash = importJar(storage)
val classLoader = AttachmentsClassLoader(arrayOf(attachmentHash).map { storage.openAttachment(it)!! })
val attachedClass = Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, classLoader)
CordaClassResolver(EmptyWhitelist).getRegistration(attachedClass)
}
@Test
fun `Annotation is inherited from interfaces`() {
CordaClassResolver(EmptyWhitelist).getRegistration(SerializableViaInterface::class.java)
CordaClassResolver(EmptyWhitelist).getRegistration(SerializableViaSubInterface::class.java)
}
@Test
fun `Annotation is inherited from superclass`() {
CordaClassResolver(EmptyWhitelist).getRegistration(SubElement::class.java)
CordaClassResolver(EmptyWhitelist).getRegistration(SubSubElement::class.java)
CordaClassResolver(EmptyWhitelist).getRegistration(SerializableViaSuperSubInterface::class.java)
}
}

View File

@ -94,9 +94,11 @@ class KryoTests {
assertEquals(-1, readRubbishStream.read()) assertEquals(-1, readRubbishStream.read())
} }
@CordaSerializable
private data class Person(val name: String, val birthday: Instant?) private data class Person(val name: String, val birthday: Instant?)
@Suppress("unused") @Suppress("unused")
@CordaSerializable
private class Cyclic(val value: Int) { private class Cyclic(val value: Int) {
val thisInstance = this val thisInstance = this
override fun equals(other: Any?): Boolean = (this === other) || (other is Cyclic && this.value == other.value) override fun equals(other: Any?): Boolean = (this === other) || (other is Cyclic && this.value == other.value)

View File

@ -15,7 +15,7 @@ class SerializationTokenTest {
@Before @Before
fun setup() { fun setup() {
kryo = THREAD_LOCAL_KRYO.get() kryo = threadLocalStorageKryo()
} }
@After @After

View File

@ -4,7 +4,7 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoSerializable import com.esotericsoftware.kryo.KryoSerializable
import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import net.corda.core.serialization.createKryo import net.corda.core.serialization.createInternalKryo
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -106,7 +106,7 @@ class ProgressTrackerTest {
} }
} }
val kryo = createKryo().apply { val kryo = createInternalKryo().apply {
// This is required to make sure Kryo walks through the auto-generated members for the lambda below. // This is required to make sure Kryo walks through the auto-generated members for the lambda below.
fieldSerializerConfig.isIgnoreSyntheticFields = false fieldSerializerConfig.isIgnoreSyntheticFields = false
} }

View File

@ -88,17 +88,15 @@ Wire protocol
The client RPC wire protocol is not currently documented. To use it you must use the client library provided. The client RPC wire protocol is not currently documented. To use it you must use the client library provided.
This is likely to change in a future release. This is likely to change in a future release.
Registering classes with RPC Kryo Whitelisting classes with the Corda node
--------------------------------- ----------------------------------------
In the present implementation of the node we use Kryo to generate the *on the wire* representation of contracts states To avoid the RPC interface being wide open to all
or any other classes that form part of the RPC arguments or response. To avoid the RPC interface being wide open to all classes on the classpath, Cordapps have to whitelist any classes they require with the serialization framework of Corda,
classes on the classpath, Cordapps will currently have to register any classes or custom serialisers they require with Kryo if they are not one of those whitelisted by default in ``DefaultWhitelist``, via either the plugin architecture or simply
if they are not one of those registered by default in ``RPCKryo`` via the plugin architecture. See :doc:`creating-a-cordapp`. with the annotation ``@CordaSerializable``. See :doc:`creating-a-cordapp` or :doc:`serialization`. An example is shown in :doc:`tutorial-clientrpc-api`.
This will require some familiarity with Kryo. An example is shown in :doc:`tutorial-clientrpc-api`.
.. warning:: We will be replacing the use of Kryo in RPC with a stable message format and this will mean that this plugin .. warning:: We will be replacing the use of Kryo in the serialization framework and so additional changes here are likely.
customisation point will either go away completely or change.
.. _CordaRPCClient: api/kotlin/corda/net.corda.client/-corda-r-p-c-client/index.html .. _CordaRPCClient: api/kotlin/corda/net.corda.client/-corda-r-p-c-client/index.html
.. _CordaRPCOps: api/kotlin/corda/net.corda.node.services.messaging/-corda-r-p-c-ops/index.html .. _CordaRPCOps: api/kotlin/corda/net.corda.node.services.messaging/-corda-r-p-c-ops/index.html

View File

@ -91,9 +91,10 @@ extensions to be created, or registered at startup. In particular:
functions inside the node, for instance to initiate workflows when functions inside the node, for instance to initiate workflows when
certain conditions are met. certain conditions are met.
e. The ``registerRPCKryoTypes`` function allows custom Kryo serialisers e. The ``customizeSerialization`` function allows classes to be whitelisted
to be registered and whitelisted for the RPC client interface. For for object serialisation, over and above those tagged with the ``@CordaSerializable``
instance new state types passed to flows started via RPC will need annotation. In general the annotation should be preferred. For
to be explicitly registered. This will be called at various points on instance new state types will need to be explicitly registered. This will be called at
various threads and needs to be stable and thread safe. various points on various threads and needs to be stable and thread safe. See
:doc:`serialization`.

View File

@ -9,14 +9,15 @@ App plugins
.. note:: Currently apps are only supported for JVM languages. .. note:: Currently apps are only supported for JVM languages.
To create an app plugin you must you must extend from `CordaPluginRegistry`_. The JavaDoc contains To create an app plugin you must extend from `CordaPluginRegistry`_. The JavaDoc contains
specific details of the implementation, but you can extend the server in the following ways: specific details of the implementation, but you can extend the server in the following ways:
1. Required flows: Specify which flows will be whitelisted for use in your RPC calls. 1. Required flows: Specify which flows will be whitelisted for use in your RPC calls.
2. Service plugins: Register your services (see below). 2. Service plugins: Register your services (see below).
3. Web APIs: You may register your own endpoints under /api/ of the bundled web server. 3. Web APIs: You may register your own endpoints under /api/ of the bundled web server.
4. Static web endpoints: You may register your own static serving directories for serving web content from the web server. 4. Static web endpoints: You may register your own static serving directories for serving web content from the web server.
5. Registering your additional classes used in RPC. 5. Whitelisting your additional contract, state and other classes for object serialization. Any class that forms part
of a persisted state, that is used in messaging between flows or in RPC needs to be whitelisted.
Services Services
-------- --------

View File

@ -1,6 +1,5 @@
package net.corda.docs package net.corda.docs
import com.esotericsoftware.kryo.Kryo
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued import net.corda.core.contracts.Issued
@ -10,7 +9,9 @@ import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.SerializationCustomization
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashExitFlow import net.corda.flows.CashExitFlow
import net.corda.flows.CashIssueFlow import net.corda.flows.CashIssueFlow
@ -132,12 +133,17 @@ fun generateTransactions(proxy: CordaRPCOps) {
// END 6 // END 6
// START 7 // START 7
// Not annotated, so need to whitelist manually.
data class ExampleRPCValue(val foo: String) data class ExampleRPCValue(val foo: String)
// Annotated, so no need to whitelist manually.
@CordaSerializable
data class ExampleRPCValue2(val bar: Int)
class ExampleRPCCordaPluginRegistry : CordaPluginRegistry() { class ExampleRPCCordaPluginRegistry : CordaPluginRegistry() {
override fun registerRPCKryoTypes(kryo: Kryo): Boolean { override fun customizeSerialization(custom: SerializationCustomization): Boolean {
// Add classes like this. // Add classes like this.
kryo.register(ExampleRPCValue::class.java) custom.addToWhitelist(ExampleRPCValue::class.java)
// You should return true, otherwise your plugin will be ignored for registering classes with Kryo. // You should return true, otherwise your plugin will be ignored for registering classes with Kryo.
return true return true
} }

View File

@ -14,6 +14,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.unconsumedStates import net.corda.core.node.services.unconsumedStates
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.flows.FinalityFlow import net.corda.flows.FinalityFlow
@ -27,12 +28,14 @@ object FxTransactionDemoTutorial {
} }
} }
@CordaSerializable
private data class FxRequest(val tradeId: String, private data class FxRequest(val tradeId: String,
val amount: Amount<Issued<Currency>>, val amount: Amount<Issued<Currency>>,
val owner: Party, val owner: Party,
val counterparty: Party, val counterparty: Party,
val notary: Party? = null) val notary: Party? = null)
@CordaSerializable
private data class FxResponse(val inputs: List<StateAndRef<Cash.State>>, private data class FxResponse(val inputs: List<StateAndRef<Cash.State>>,
val outputs: List<Cash.State>) val outputs: List<Cash.State>)

View File

@ -7,6 +7,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.linearHeadsOfType import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.flows.FinalityFlow import net.corda.flows.FinalityFlow
@ -32,6 +33,7 @@ inline fun <reified T : LinearState> ServiceHub.latest(ref: StateRef): StateAndR
// DOCEND 1 // DOCEND 1
// Minimal state model of a manual approval process // Minimal state model of a manual approval process
@CordaSerializable
enum class WorkflowState { enum class WorkflowState {
NEW, NEW,
APPROVED, APPROVED,

View File

@ -117,6 +117,7 @@ each side.
} }
// This object is serialised to the network and is the first flow message the seller sends to the buyer. // This object is serialised to the network and is the first flow message the seller sends to the buyer.
@CordaSerializable
data class SellerTradeInfo( data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>, val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>, val price: Amount<Currency>,
@ -188,6 +189,15 @@ and try again.
.. note:: Java 9 is likely to remove this pre-marking requirement completely. .. note:: Java 9 is likely to remove this pre-marking requirement completely.
Whitelisted classes with the Corda node
---------------------------------------
For security reasons, we do not want Corda nodes to be able to receive instances of any class on the classpath
via messaging, since this has been exploited in other Java application containers in the past. Instead, we require
that every class contained in messages is whitelisted. Some classes are whitelisted by default (see ``DefaultWhitelist``),
but others outside of that set need to be whitelisted either by using the annotation ``@CordaSerializable`` or via the
plugin framework. See :doc:`serialization`. You can see above that the ``SellerTradeInfo`` has been annotated.
Starting your flow Starting your flow
------------------ ------------------

View File

@ -59,6 +59,8 @@ R3
The consortium behind Corda The consortium behind Corda
SIMM SIMM
Standard Initial Margin Model. A way of determining a counterparty's margin payment to another counterparty based on a collection of trades such that, in the event of default, the receiving counterparty has limited exposure. Standard Initial Margin Model. A way of determining a counterparty's margin payment to another counterparty based on a collection of trades such that, in the event of default, the receiving counterparty has limited exposure.
Serialization
Object serialization is the process of converting objects into a stream of bytes and, deserialization, the reverse process.
Service Hub Service Hub
A hub in each Corda node that manages the services upon which other components of the node depend. Services may include facilities for identity management, storage management, network map management etc. A hub in each Corda node that manages the services upon which other components of the node depend. Services may include facilities for identity management, storage management, network map management etc.
Signed Transaction Signed Transaction
@ -71,3 +73,5 @@ UTXO
Unspent Transaction Output. First introduced by the bitcoin model, an unspent transaction is data that has been output from a transaction but not yet used in another transaction. Unspent Transaction Output. First introduced by the bitcoin model, an unspent transaction is data that has been output from a transaction but not yet used in another transaction.
Verify Verify
To confirm that the transaction is valid by ensuring the the outputs are correctly derived from the inputs combined with the command of the transaction. To confirm that the transaction is valid by ensuring the the outputs are correctly derived from the inputs combined with the command of the transaction.
Whitelisting
To indicate that a class is intended to be passed between nodes or between a node and an RPC client, it is added to a whitelist. This prevents the node presenting a large surface area of all classes in all dependencies of the node as containing possible vulnerabilities.

View File

@ -70,6 +70,7 @@ Documentation Contents:
:maxdepth: 2 :maxdepth: 2
:caption: The Corda node :caption: The Corda node
serialization
clientrpc clientrpc
messaging messaging
persistence persistence

View File

@ -12,6 +12,13 @@ Milestone 9
* Split ``CashFlow`` into individual ``CashIssueFlow``, ``CashPaymentFlow`` and ``CashExitFlow`` flows, so that fine * Split ``CashFlow`` into individual ``CashIssueFlow``, ``CashPaymentFlow`` and ``CashExitFlow`` flows, so that fine
grained permissions can be applied. Added ``CashFlowCommand`` for use-cases where cash flow triggers need to be grained permissions can be applied. Added ``CashFlowCommand`` for use-cases where cash flow triggers need to be
captured in an object that can be passed around. captured in an object that can be passed around.
* ``CordaPluginRegistry`` method ``registerRPCKryoTypes`` is renamed ``customizeSerialization`` and the argument
types now hide the presence of Kryo.
* Object Serialization:
* Consolidated Kryo implementations across RPC and P2P messaging with whitelisting of classes via plugins or with
``@CordaSerializable`` for added node security.
Milestone 8 Milestone 8
----------- -----------

View File

@ -0,0 +1,39 @@
Object Serialization
====================
What is serialization (and deserialization)?
--------------------------------------------
Object serialization is the process of converting objects into a stream of bytes and, deserialization, the reverse
process of creating objects from a stream of bytes. It takes place every time nodes pass objects to each other as
messages, when objects are sent to or from RPC clients from the node, and when we store transactions in the database.
Whitelisting
------------
In classic Java serialization, any class on the JVM classpath can be deserialized. This has shown to be a source of exploits
and vulnerabilities by exploiting the large set of 3rd party libraries on the classpath as part of the dependencies of
a JVM application and a carefully crafted stream of bytes to be deserialized. In Corda, we prevent just any class from
being deserialized (and pro-actively during serialization) by insisting that each object's class belongs on a whitelist
of allowed classes.
Classes get onto the whitelist via one of three mechanisms:
#. Via the ``@CordaSerializable`` annotation. In order to whitelist a class, this annotation can be present on the
class itself, on any of the super classes or on any interface implemented by the class or super classes or any
interface extended by an interface implemented by the class or superclasses.
#. By returning the class as part of a plugin via the method ``customizeSerialization``. It's important to return
true from this method if you override it, otherwise the plugin will be excluded. See :doc:`corda-plugins`.
#. Via the built in Corda whitelist (see the class ``DefaultWhitelist``). Whilst this is not user editable, it does list
common JDK classes that have been whitelisted for your convenience.
The annotation is the preferred method for whitelisting. An example is shown in :doc:`tutorial-clientrpc-api`.
It's reproduced here as an example of both ways you can do this for a couple of example classes.
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
:language: kotlin
:start-after: START 7
:end-before: END 7
.. note:: Several of the core interfaces at the heart of Corda are already annotated and so any classes that implement
them will automatically be whitelisted. This includes `Contract`, `ContractState` and `CommandData`.

View File

@ -85,11 +85,11 @@ Now let's try to visualise the transaction graph. We will use a graph drawing li
If we run the client with ``Visualise`` we should see a simple random graph being drawn as new transactions are being created. If we run the client with ``Visualise`` we should see a simple random graph being drawn as new transactions are being created.
Registering classes from your CorDapp with RPC Kryo Whitelisting classes from your CorDapp with the Corda node
--------------------------------------------------- ----------------------------------------------------------
As described in :doc:`clientrpc`, you currently have to register any additional classes you add that are needed in RPC As described in :doc:`clientrpc`, you have to whitelist any additional classes you add that are needed in RPC
requests or responses with the `Kryo` instance RPC uses. Here's an example of how you do this for an example class. requests or responses with the Corda node. Here's an example of both ways you can do this for a couple of example classes.
.. literalinclude:: example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt .. literalinclude:: example-code/src/main/kotlin/net/corda/docs/ClientRpcTutorial.kt
:language: kotlin :language: kotlin
@ -98,8 +98,7 @@ requests or responses with the `Kryo` instance RPC uses. Here's an example of h
See more on plugins in :doc:`creating-a-cordapp`. See more on plugins in :doc:`creating-a-cordapp`.
.. warning:: We will be replacing the use of Kryo in RPC with a stable message format and this will mean that this plugin .. warning:: We will be replacing the use of Kryo in the serialization framework and so additional changes here are likely.
customisation point will either go away completely or change.
Security Security
-------- --------

View File

@ -2,10 +2,12 @@ package net.corda.contracts.universal
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 net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
@CordaSerializable
interface Arrangement interface Arrangement
// A base arrangement with no rights and no obligations. Contract cancellation/termination is a transition to ``Zero``. // A base arrangement with no rights and no obligations. Contract cancellation/termination is a transition to ``Zero``.
@ -30,6 +32,7 @@ data class Obligation(val amount: Perceivable<BigDecimal>, val currency: Currenc
// The ``And`` combinator cannot be root in a arrangement. // The ``And`` combinator cannot be root in a arrangement.
data class And(val arrangements: Set<Arrangement>) : Arrangement data class And(val arrangements: Set<Arrangement>) : Arrangement
@CordaSerializable
data class Action(val name: String, val condition: Perceivable<Boolean>, val arrangement: Arrangement) data class Action(val name: String, val condition: Perceivable<Boolean>, val arrangement: Arrangement)
// An action combinator. This declares a list of named action that can be taken by anyone of the actors given that // An action combinator. This declares a list of named action that can be taken by anyone of the actors given that

View File

@ -4,14 +4,17 @@ import net.corda.core.contracts.BusinessCalendar
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.serialization.CordaSerializable
import java.lang.reflect.Type import java.lang.reflect.Type
import java.math.BigDecimal import java.math.BigDecimal
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.util.* import java.util.*
@CordaSerializable
interface Perceivable<T> interface Perceivable<T>
@CordaSerializable
enum class Comparison { enum class Comparison {
LT, LTE, GT, GTE LT, LTE, GT, GTE
} }
@ -127,6 +130,7 @@ infix fun Perceivable<BigDecimal>.gte(n: BigDecimal) = perceivableComparison(thi
infix fun Perceivable<BigDecimal>.lte(n: Double) = perceivableComparison(this, Comparison.LTE, const(BigDecimal(n))) infix fun Perceivable<BigDecimal>.lte(n: Double) = perceivableComparison(this, Comparison.LTE, const(BigDecimal(n)))
infix fun Perceivable<BigDecimal>.gte(n: Double) = perceivableComparison(this, Comparison.GTE, const(BigDecimal(n))) infix fun Perceivable<BigDecimal>.gte(n: Double) = perceivableComparison(this, Comparison.GTE, const(BigDecimal(n)))
@CordaSerializable
enum class Operation { enum class Operation {
PLUS, MINUS, TIMES, DIV PLUS, MINUS, TIMES, DIV
} }

View File

@ -1,22 +1,34 @@
package net.corda.contracts; package net.corda.contracts;
import com.google.common.collect.*; import com.google.common.collect.ImmutableList;
import kotlin.*; import kotlin.Pair;
import net.corda.contracts.asset.*; import kotlin.Unit;
import net.corda.contracts.asset.CashKt;
import net.corda.core.contracts.*; import net.corda.core.contracts.*;
import net.corda.core.contracts.TransactionForContract.*; import net.corda.core.contracts.TransactionForContract.InOutGroup;
import net.corda.core.contracts.clauses.*; import net.corda.core.contracts.clauses.AnyOf;
import net.corda.core.crypto.*; 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 org.jetbrains.annotations.*; import net.corda.core.crypto.CompositeKey;
import net.corda.core.crypto.CryptoUtilities;
import net.corda.core.crypto.Party;
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.time.*; import java.time.Instant;
import java.util.*; import java.util.Collections;
import java.util.stream.*; import java.util.Currency;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static kotlin.collections.CollectionsKt.*; import static kotlin.collections.CollectionsKt.single;
import static net.corda.core.contracts.ContractsDSL.*; import static net.corda.core.contracts.ContractsDSL.requireSingleCommand;
import static net.corda.core.contracts.ContractsDSL.requireThat;
/** /**

View File

@ -47,6 +47,7 @@ class CommercialPaperLegacy : Contract {
interface Commands : CommandData { interface Commands : CommandData {
class Move : TypeOnlyCommandData(), Commands class Move : TypeOnlyCommandData(), Commands
class Redeem : TypeOnlyCommandData(), Commands class Redeem : TypeOnlyCommandData(), Commands
// We don't need a nonce in the issue command, because the issuance.reference field should already be unique per CP. // We don't need a nonce in the issue command, because the issuance.reference field should already be unique per CP.
// However, nothing in the platform enforces that uniqueness: it's up to the issuer. // However, nothing in the platform enforces that uniqueness: it's up to the issuer.

View File

@ -12,6 +12,7 @@ import net.corda.core.crypto.*
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.serialization.CordaSerializable
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
@ -74,6 +75,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Issue::class.java) override val requiredCommands: Set<Class<out CommandData>> = setOf(Commands.Issue::class.java)
} }
@CordaSerializable
class ConserveAmount : AbstractConserveAmount<State, Commands, Currency>() class ConserveAmount : AbstractConserveAmount<State, Commands, Currency>()
} }

View File

@ -11,6 +11,7 @@ import net.corda.core.crypto.CompositeKey
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.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import java.util.* import java.util.*
@ -86,6 +87,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
/** /**
* Standard clause for conserving the amount from input to output. * Standard clause for conserving the amount from input to output.
*/ */
@CordaSerializable
class ConserveAmount : AbstractConserveAmount<State, Commands, Commodity>() class ConserveAmount : AbstractConserveAmount<State, Commands, Commodity>()
} }
@ -112,6 +114,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
} }
// Just for grouping // Just for grouping
@CordaSerializable
interface Commands : FungibleAsset.Commands { interface Commands : FungibleAsset.Commands {
/** /**
* A command stating that money has been moved, optionally to fulfil another contract. * A command stating that money has been moved, optionally to fulfil another contract.

View File

@ -7,6 +7,7 @@ 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.serialization.CordaSerializable
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.core.utilities.NonEmptySet import net.corda.core.utilities.NonEmptySet
@ -227,6 +228,7 @@ class Obligation<P> : Contract {
* to the state. Most states will not leave the [NORMAL] lifecycle. Note that settled (as an end lifecycle) is * to the state. Most states will not leave the [NORMAL] lifecycle. Note that settled (as an end lifecycle) is
* represented by absence of the state on transaction output. * represented by absence of the state on transaction output.
*/ */
@CordaSerializable
enum class Lifecycle { enum class Lifecycle {
/** Default lifecycle state for a contract, in which it can be settled normally */ /** Default lifecycle state for a contract, in which it can be settled normally */
NORMAL, NORMAL,
@ -242,6 +244,7 @@ class Obligation<P> : Contract {
* *
* @param P the product the obligation is for payment of. * @param P the product the obligation is for payment of.
*/ */
@CordaSerializable
data class Terms<P>( data class Terms<P>(
/** The hash of the asset contract we're willing to accept in payment for this debt. */ /** The hash of the asset contract we're willing to accept in payment for this debt. */
val acceptableContracts: NonEmptySet<SecureHash>, val acceptableContracts: NonEmptySet<SecureHash>,
@ -323,6 +326,7 @@ class Obligation<P> : Contract {
} }
// Just for grouping // Just for grouping
@CordaSerializable
interface Commands : FungibleAsset.Commands { interface Commands : FungibleAsset.Commands {
/** /**
* Net two or more obligation states together in a close-out netting style. Limited to bilateral netting * Net two or more obligation states together in a close-out netting style. Limited to bilateral netting

View File

@ -2,7 +2,10 @@ package net.corda.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.* import net.corda.core.contracts.Amount
import net.corda.core.contracts.InsufficientBalanceException
import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.issuedBy
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.node.services.unconsumedStates import net.corda.core.node.services.unconsumedStates

View File

@ -3,7 +3,6 @@ package net.corda.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.TransactionType import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.issuedBy import net.corda.core.contracts.issuedBy
import net.corda.core.crypto.Party import net.corda.core.crypto.Party

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.Party
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
@ -20,6 +21,7 @@ import java.util.*
* useful for creation of fake assets. * useful for creation of fake assets.
*/ */
object IssuerFlow { object IssuerFlow {
@CordaSerializable
data class IssuanceRequestState(val amount: Amount<Currency>, val issueToParty: Party, val issuerPartyRef: OpaqueBytes) data class IssuanceRequestState(val amount: Amount<Currency>, val issueToParty: Party, val issuerPartyRef: OpaqueBytes)
/** /**

View File

@ -8,6 +8,7 @@ import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction 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
@ -39,17 +40,20 @@ object TwoPartyTradeFlow {
// and [AbstractStateReplacementFlow]. // and [AbstractStateReplacementFlow].
class UnacceptablePriceException(givenPrice: Amount<Currency>) : FlowException("Unacceptable price: $givenPrice") class UnacceptablePriceException(givenPrice: Amount<Currency>) : FlowException("Unacceptable price: $givenPrice")
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : FlowException() { class AssetMismatchException(val expectedTypeName: String, val typeName: String) : FlowException() {
override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName" override fun toString() = "The submitted asset didn't match the expected type: $expectedTypeName vs $typeName"
} }
// This object is serialised to the network and is the first flow message the seller sends to the buyer. // This object is serialised to the network and is the first flow message the seller sends to the buyer.
@CordaSerializable
data class SellerTradeInfo( data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>, val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>, val price: Amount<Currency>,
val sellerOwnerKey: CompositeKey val sellerOwnerKey: CompositeKey
) )
@CordaSerializable
data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey, data class SignaturesFromSeller(val sellerSig: DigitalSignature.WithKey,
val notarySig: DigitalSignature.WithKey) val notarySig: DigitalSignature.WithKey)

View File

@ -8,6 +8,7 @@ import net.corda.core.messaging.SingleMessageRecipient
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
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.flows.ServiceRequestMessage import net.corda.flows.ServiceRequestMessage
@ -118,6 +119,7 @@ class P2PMessagingTest : NodeBasedTest() {
return net.sendRequest<Any>(javaClass.name, request, target) return net.sendRequest<Any>(javaClass.name, request, target)
} }
@CordaSerializable
private data class TestRequest(override val sessionID: Long = random63BitValue(), private data class TestRequest(override val sessionID: Long = random63BitValue(),
override val replyTo: SingleMessageRecipient) : ServiceRequestMessage override val replyTo: SingleMessageRecipient) : ServiceRequestMessage
} }

View File

@ -0,0 +1,52 @@
package net.corda.node.serialization
import com.esotericsoftware.kryo.KryoException
import com.google.common.net.HostAndPort
import net.corda.core.node.CordaPluginRegistry
import net.corda.core.serialization.SerializationCustomization
import org.apache.activemq.artemis.api.core.SimpleString
import rx.Notification
import java.math.BigDecimal
import java.time.LocalDate
import java.time.Period
import java.util.*
class DefaultWhitelist : CordaPluginRegistry() {
override fun customizeSerialization(custom: SerializationCustomization): Boolean {
custom.apply {
addToWhitelist(Array<Any>(0, {}).javaClass)
addToWhitelist(Notification::class.java)
addToWhitelist(Notification.Kind::class.java)
addToWhitelist(ArrayList::class.java)
addToWhitelist(listOf<Any>().javaClass) // EmptyList
addToWhitelist(Pair::class.java)
addToWhitelist(ByteArray::class.java)
addToWhitelist(UUID::class.java)
addToWhitelist(LinkedHashSet::class.java)
addToWhitelist(setOf<Unit>().javaClass) // EmptySet
addToWhitelist(Currency::class.java)
addToWhitelist(listOf(Unit).javaClass) // SingletonList
addToWhitelist(setOf(Unit).javaClass) // SingletonSet
addToWhitelist(mapOf(Unit to Unit).javaClass) // SingletonSet
addToWhitelist(HostAndPort::class.java)
addToWhitelist(SimpleString::class.java)
addToWhitelist(KryoException::class.java)
addToWhitelist(StringBuffer::class.java)
addToWhitelist(Unit::class.java)
addToWhitelist(java.io.ByteArrayInputStream::class.java)
addToWhitelist(java.lang.Class::class.java)
addToWhitelist(java.math.BigDecimal::class.java)
addToWhitelist(java.security.KeyPair::class.java)
addToWhitelist(java.time.Duration::class.java)
addToWhitelist(java.time.Instant::class.java)
addToWhitelist(java.time.LocalDate::class.java)
addToWhitelist(java.util.Collections.singletonMap("A", "B").javaClass)
addToWhitelist(java.util.HashMap::class.java)
addToWhitelist(java.util.LinkedHashMap::class.java)
addToWhitelist(BigDecimal::class.java)
addToWhitelist(LocalDate::class.java)
addToWhitelist(Period::class.java)
}
return true
}
}

View File

@ -7,6 +7,7 @@ import net.corda.core.messaging.MessageRecipientGroup
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.read import net.corda.core.read
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.services.config.SSLConfiguration import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Inbound import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Inbound
@ -65,6 +66,7 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
val hostAndPort: HostAndPort val hostAndPort: HostAndPort
} }
@CordaSerializable
data class NetworkMapAddress(override val hostAndPort: HostAndPort) : SingleMessageRecipient, ArtemisPeerAddress { data class NetworkMapAddress(override val hostAndPort: HostAndPort) : SingleMessageRecipient, ArtemisPeerAddress {
override val queueName: String get() = NETWORK_MAP_QUEUE override val queueName: String get() = NETWORK_MAP_QUEUE
} }
@ -80,6 +82,7 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
* @param queueName The name of the queue this address is associated with. * @param queueName The name of the queue this address is associated with.
* @param hostAndPort The address of the node. * @param hostAndPort The address of the node.
*/ */
@CordaSerializable
data class NodeAddress(override val queueName: String, override val hostAndPort: HostAndPort) : ArtemisPeerAddress { data class NodeAddress(override val queueName: String, override val hostAndPort: HostAndPort) : ArtemisPeerAddress {
companion object { companion object {
fun asPeer(peerIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress { fun asPeer(peerIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress {

View File

@ -3,50 +3,22 @@
package net.corda.node.services.messaging package net.corda.node.services.messaging
import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Registration import com.esotericsoftware.kryo.Registration
import com.esotericsoftware.kryo.Serializer import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.*
import net.corda.contracts.asset.Cash
import net.corda.core.ErrorOr
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.flows.IllegalFlowLogicException
import net.corda.core.flows.StateMachineRunId
import net.corda.core.messaging.FlowHandle
import net.corda.core.messaging.StateMachineInfo
import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.node.*
import net.corda.core.node.services.*
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.toObservable import net.corda.core.toObservable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.node.internal.AbstractNode
import net.corda.node.services.User import net.corda.node.services.User
import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NODE_USER import net.corda.node.services.messaging.ArtemisMessagingComponent.Companion.NODE_USER
import net.corda.node.services.messaging.ArtemisMessagingComponent.NetworkMapAddress
import net.corda.node.services.statemachine.FlowSessionException
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.commons.fileupload.MultipartStream import org.apache.commons.fileupload.MultipartStream
import org.objenesis.strategy.StdInstantiatorStrategy
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import rx.Notification import rx.Notification
import rx.Observable import rx.Observable
import java.io.BufferedInputStream
import java.time.Instant
import java.util.*
/** Global RPC logger */ /** Global RPC logger */
val rpcLog: Logger by lazy { LoggerFactory.getLogger("net.corda.rpc") } val rpcLog: Logger by lazy { LoggerFactory.getLogger("net.corda.rpc") }
@ -95,6 +67,7 @@ fun requirePermission(permission: String) {
* Thrown to indicate a fatal error in the RPC system itself, as opposed to an error generated by the invoked * Thrown to indicate a fatal error in the RPC system itself, as opposed to an error generated by the invoked
* method. * method.
*/ */
@CordaSerializable
open class RPCException(msg: String, cause: Throwable?) : RuntimeException(msg, cause) { open class RPCException(msg: String, cause: Throwable?) : RuntimeException(msg, cause) {
constructor(msg: String) : this(msg, null) constructor(msg: String) : this(msg, null)
@ -112,129 +85,20 @@ object ClassSerializer : Serializer<Class<*>>() {
} }
} }
@CordaSerializable
class PermissionException(msg: String) : RuntimeException(msg) class PermissionException(msg: String) : RuntimeException(msg)
// The Kryo used for the RPC wire protocol. Every type in the wire protocol is listed here explicitly. // The Kryo used for the RPC wire protocol. Every type in the wire protocol is listed here explicitly.
// This is annoying to write out, but will make it easier to formalise the wire protocol when the time comes, // This is annoying to write out, but will make it easier to formalise the wire protocol when the time comes,
// because we can see everything we're using in one place. // because we can see everything we're using in one place.
private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null) : Kryo() { private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null) : CordaKryo(makeStandardClassResolver()) {
companion object {
private val pluginRegistries: List<CordaPluginRegistry> by lazy {
val unusedKryo = Kryo()
// Sorting required to give a stable ordering, as Kryo allocates integer tokens for each registered class.
ServiceLoader.load(CordaPluginRegistry::class.java).toList().filter { it.registerRPCKryoTypes(unusedKryo) }.sortedBy { it.javaClass.name }
}
}
init { init {
isRegistrationRequired = true DefaultKryoCustomizer.customize(this)
// Allow construction of objects using a JVM backdoor that skips invoking the constructors, if there is no
// no-arg constructor available.
instantiatorStrategy = Kryo.DefaultInstantiatorStrategy(StdInstantiatorStrategy())
register(Arrays.asList("").javaClass, ArraysAsListSerializer()) // RPC specific classes
register(Instant::class.java, ReferencesAwareJavaSerializer)
register(SignedTransaction::class.java, ImmutableClassSerializer(SignedTransaction::class))
register(WireTransaction::class.java, WireTransactionSerializer)
register(SerializedBytes::class.java, SerializedBytesSerializer)
register(AnonymousParty::class.java)
register(Party::class.java)
register(Array<Any>(0,{}).javaClass)
register(Class::class.java, ClassSerializer) register(Class::class.java, ClassSerializer)
UnmodifiableCollectionsSerializer.registerSerializers(this)
ImmutableListSerializer.registerSerializers(this)
ImmutableSetSerializer.registerSerializers(this)
ImmutableSortedSetSerializer.registerSerializers(this)
ImmutableMapSerializer.registerSerializers(this)
ImmutableMultimapSerializer.registerSerializers(this)
register(BufferedInputStream::class.java, InputStreamSerializer)
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer)
register(MultipartStream.ItemInputStream::class.java, InputStreamSerializer) register(MultipartStream.ItemInputStream::class.java, InputStreamSerializer)
noReferencesWithin<WireTransaction>()
register(ErrorOr::class.java)
register(MarshalledObservation::class.java, ImmutableClassSerializer(MarshalledObservation::class)) register(MarshalledObservation::class.java, ImmutableClassSerializer(MarshalledObservation::class))
register(Notification::class.java)
register(Notification.Kind::class.java)
register(ArrayList::class.java)
register(listOf<Any>().javaClass) // EmptyList
register(IllegalStateException::class.java)
register(Pair::class.java)
register(StateMachineUpdate.Added::class.java)
register(StateMachineUpdate.Removed::class.java)
register(StateMachineInfo::class.java)
register(DigitalSignature.WithKey::class.java)
register(DigitalSignature.LegallyIdentifiable::class.java)
register(ByteArray::class.java)
register(EdDSAPublicKey::class.java, Ed25519PublicKeySerializer)
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
register(CompositeKey.Leaf::class.java)
register(CompositeKey.Node::class.java)
register(Vault::class.java)
register(Vault.Update::class.java)
register(StateMachineRunId::class.java)
register(StateMachineTransactionMapping::class.java)
register(UUID::class.java)
register(UniqueIdentifier::class.java)
register(LinkedHashSet::class.java)
register(LinkedHashMap::class.java)
register(StateAndRef::class.java)
register(setOf<Unit>().javaClass) // EmptySet
register(StateRef::class.java)
register(SecureHash.SHA256::class.java)
register(TransactionState::class.java)
register(Cash.State::class.java)
register(Amount::class.java)
register(Issued::class.java)
register(PartyAndReference::class.java)
register(OpaqueBytes::class.java)
register(Currency::class.java)
register(Cash::class.java)
register(Cash.Clauses.ConserveAmount::class.java)
register(listOf(Unit).javaClass) // SingletonList
register(setOf(Unit).javaClass) // SingletonSet
register(ServiceEntry::class.java)
register(NodeInfo::class.java)
register(PhysicalLocation::class.java)
register(NetworkMapCache.MapChange.Added::class.java)
register(NetworkMapCache.MapChange.Removed::class.java)
register(NetworkMapCache.MapChange.Modified::class.java)
register(ArtemisMessagingComponent.NodeAddress::class.java)
register(NetworkMapAddress::class.java)
register(ServiceInfo::class.java)
register(ServiceType.getServiceType("ab", "ab").javaClass)
register(ServiceType.parse("ab").javaClass)
register(WorldCoordinate::class.java)
register(HostAndPort::class.java)
register(SimpleString::class.java)
register(ServiceEntry::class.java)
// Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway.
register(Array<StackTraceElement>::class, read = { kryo, input -> emptyArray() }, write = { kryo, output, obj -> })
register(FlowException::class.java)
register(FlowSessionException::class.java)
register(IllegalFlowLogicException::class.java)
register(RuntimeException::class.java)
register(IllegalArgumentException::class.java)
register(ArrayIndexOutOfBoundsException::class.java)
register(IndexOutOfBoundsException::class.java)
register(NoSuchElementException::class.java)
register(RPCException::class.java)
register(PermissionException::class.java)
register(Throwable::class.java)
register(FlowHandle::class.java)
register(KryoException::class.java)
register(StringBuffer::class.java)
register(Unit::class.java)
for ((_flow, argumentTypes) in AbstractNode.defaultFlowWhiteList) {
for (type in argumentTypes) {
register(type)
}
}
pluginRegistries.forEach { it.registerRPCKryoTypes(this) }
} }
// TODO: workaround to prevent Observable registration conflict when using plugin registered kyro classes // TODO: workaround to prevent Observable registration conflict when using plugin registered kyro classes

View File

@ -16,6 +16,7 @@ import net.corda.core.node.services.DEFAULT_SESSION_ID
import net.corda.core.node.services.NetworkMapCache import net.corda.core.node.services.NetworkMapCache
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.random63BitValue import net.corda.core.random63BitValue
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
@ -73,27 +74,33 @@ interface NetworkMapService {
override val replyTo: SingleMessageRecipient, override val replyTo: SingleMessageRecipient,
override val sessionID: Long = random63BitValue()) : ServiceRequestMessage override val sessionID: Long = random63BitValue()) : ServiceRequestMessage
@CordaSerializable
data class FetchMapResponse(val nodes: Collection<NodeRegistration>?, val version: Int) data class FetchMapResponse(val nodes: Collection<NodeRegistration>?, val version: Int)
class QueryIdentityRequest(val identity: Party, class QueryIdentityRequest(val identity: Party,
override val replyTo: SingleMessageRecipient, override val replyTo: SingleMessageRecipient,
override val sessionID: Long) : ServiceRequestMessage override val sessionID: Long) : ServiceRequestMessage
@CordaSerializable
data class QueryIdentityResponse(val node: NodeInfo?) data class QueryIdentityResponse(val node: NodeInfo?)
class RegistrationRequest(val wireReg: WireNodeRegistration, class RegistrationRequest(val wireReg: WireNodeRegistration,
override val replyTo: SingleMessageRecipient, override val replyTo: SingleMessageRecipient,
override val sessionID: Long = random63BitValue()) : ServiceRequestMessage override val sessionID: Long = random63BitValue()) : ServiceRequestMessage
@CordaSerializable
data class RegistrationResponse(val success: Boolean) data class RegistrationResponse(val success: Boolean)
class SubscribeRequest(val subscribe: Boolean, class SubscribeRequest(val subscribe: Boolean,
override val replyTo: SingleMessageRecipient, override val replyTo: SingleMessageRecipient,
override val sessionID: Long = random63BitValue()) : ServiceRequestMessage override val sessionID: Long = random63BitValue()) : ServiceRequestMessage
@CordaSerializable
data class SubscribeResponse(val confirmed: Boolean) data class SubscribeResponse(val confirmed: Boolean)
@CordaSerializable
data class Update(val wireReg: WireNodeRegistration, val mapVersion: Int, val replyTo: MessageRecipients) data class Update(val wireReg: WireNodeRegistration, val mapVersion: Int, val replyTo: MessageRecipients)
@CordaSerializable
data class UpdateAcknowledge(val mapVersion: Int, val replyTo: MessageRecipients) data class UpdateAcknowledge(val mapVersion: Int, val replyTo: MessageRecipients)
} }
@ -331,6 +338,7 @@ abstract class AbstractNetworkMapService
*/ */
// TODO: This might alternatively want to have a node and party, with the node being optional, so registering a node // TODO: This might alternatively want to have a node and party, with the node being optional, so registering a node
// involves providing both node and paerty, and deregistering a node involves a request with party but no node. // involves providing both node and paerty, and deregistering a node involves a request with party but no node.
@CordaSerializable
class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemove, var expires: Instant) { class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemove, var expires: Instant) {
/** /**
* Build a node registration in wire format. * Build a node registration in wire format.
@ -348,6 +356,7 @@ class NodeRegistration(val node: NodeInfo, val serial: Long, val type: AddOrRemo
/** /**
* A node registration and its signature as a pair. * A node registration and its signature as a pair.
*/ */
@CordaSerializable
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) {
@ -355,6 +364,7 @@ class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalS
} }
} }
@CordaSerializable
sealed class NodeMapError : Exception() { sealed class NodeMapError : Exception() {
/** Thrown if the signature on the node info does not match the public key for the identity */ /** Thrown if the signature on the node info does not match the public key for the identity */
@ -367,5 +377,8 @@ sealed class NodeMapError : Exception() {
class UnknownChangeType : NodeMapError() class UnknownChangeType : NodeMapError()
} }
@CordaSerializable
data class LastAcknowledgeInfo(val mapVersion: Int) data class LastAcknowledgeInfo(val mapVersion: Int)
@CordaSerializable
data class NodeRegistrationInfo(val reg: NodeRegistration, val mapVersion: Int) data class NodeRegistrationInfo(val reg: NodeRegistration, val mapVersion: Int)

View File

@ -4,6 +4,7 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.serialization.threadLocalStorageKryo
import net.corda.node.services.api.Checkpoint import net.corda.node.services.api.Checkpoint
import net.corda.node.services.api.CheckpointStorage import net.corda.node.services.api.CheckpointStorage
import net.corda.node.utilities.* import net.corda.node.utilities.*
@ -38,7 +39,7 @@ class DBCheckpointStorage : CheckpointStorage {
private val checkpointStorage = synchronizedMap(CheckpointMap()) private val checkpointStorage = synchronizedMap(CheckpointMap())
override fun addCheckpoint(checkpoint: Checkpoint) { override fun addCheckpoint(checkpoint: Checkpoint) {
checkpointStorage.put(checkpoint.id, checkpoint.serialize()) checkpointStorage.put(checkpoint.id, checkpoint.serialize(threadLocalStorageKryo(), true))
} }
override fun removeCheckpoint(checkpoint: Checkpoint) { override fun removeCheckpoint(checkpoint: Checkpoint) {

View File

@ -9,6 +9,7 @@ import net.corda.core.*
import net.corda.core.contracts.Attachment import net.corda.core.contracts.Attachment
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.CordaSerializable
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.services.api.AcceptsFileUpload import net.corda.node.services.api.AcceptsFileUpload
import java.io.FilterInputStream import java.io.FilterInputStream
@ -48,6 +49,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
require(storePath.isDirectory()) { "$storePath must be a directory" } require(storePath.isDirectory()) { "$storePath must be a directory" }
} }
@CordaSerializable
class OnDiskHashMismatch(val file: Path, val actual: SecureHash) : Exception() { class OnDiskHashMismatch(val file: Path, val actual: SecureHash) : Exception() {
override fun toString() = "File $file hashed to $actual: corruption in attachment store?" override fun toString() = "File $file hashed to $actual: corruption in attachment store?"
} }

View File

@ -2,8 +2,10 @@ package net.corda.node.services.statemachine
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
@CordaSerializable
interface SessionMessage interface SessionMessage
data class SessionInit(val initiatorSessionId: Long, val flowName: String, val firstPayload: Any?) : SessionMessage data class SessionInit(val initiatorSessionId: Long, val flowName: String, val firstPayload: Any?) : SessionMessage

View File

@ -369,7 +369,15 @@ class StateMachineManager(val serviceHub: ServiceHubInternal,
private fun quasarKryo(): Kryo { private fun quasarKryo(): Kryo {
val serializer = Fiber.getFiberSerializer(false) as KryoSerializer val serializer = Fiber.getFiberSerializer(false) as KryoSerializer
return createKryo(serializer.kryo) return createKryo(serializer.kryo).apply {
// Because we like to stick a Kryo object in a ThreadLocal to speed things up a bit, we can end up trying to
// serialise the Kryo object itself when suspending a fiber. That's dumb, useless AND can cause crashes, so
// we avoid it here. This is checkpointing specific.
register(Kryo::class,
read = { kryo, input -> createKryo((Fiber.getFiberSerializer() as KryoSerializer).kryo) },
write = { kryo, output, obj -> }
)
}
} }
private fun <T> createFiber(logic: FlowLogic<T>): FlowStateMachineImpl<T> { private fun <T> createFiber(logic: FlowLogic<T>): FlowStateMachineImpl<T> {

View File

@ -1,23 +1,26 @@
package net.corda.node.services.transactions package net.corda.node.services.transactions
import bftsmart.tom.ServiceProxy
import bftsmart.tom.MessageContext import bftsmart.tom.MessageContext
import bftsmart.tom.ServiceProxy
import bftsmart.tom.ServiceReplica import bftsmart.tom.ServiceReplica
import bftsmart.tom.server.defaultservices.DefaultRecoverable import bftsmart.tom.server.defaultservices.DefaultRecoverable
import bftsmart.tom.server.defaultservices.DefaultReplier import bftsmart.tom.server.defaultservices.DefaultReplier
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.node.utilities.JDBCHashMap import net.corda.node.utilities.JDBCHashMap
import net.corda.node.utilities.databaseTransaction import net.corda.node.utilities.databaseTransaction
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import java.util.LinkedHashMap import java.util.*
@CordaSerializable
enum class RequestType { enum class RequestType {
Get, Get,
Put Put
} }
/** Sent from [BFTSmartClient] to [BFTSmartServer] */ /** Sent from [BFTSmartClient] to [BFTSmartServer] */
@CordaSerializable
data class Request(val type: RequestType, val data: Any) data class Request(val type: RequestType, val data: Any)
class BFTSmartClient<K: Any, V: Any>(id: Int) { class BFTSmartClient<K: Any, V: Any>(id: Int) {

View File

@ -1,8 +1,11 @@
package net.corda.node.utilities package net.corda.node.utilities
import net.corda.core.serialization.CordaSerializable
/** /**
* Enum for when adding/removing something, for example adding or removing an entry in a directory. * Enum for when adding/removing something, for example adding or removing an entry in a directory.
*/ */
@CordaSerializable
enum class AddOrRemove { enum class AddOrRemove {
ADD, ADD,
REMOVE REMOVE

View File

@ -3,6 +3,7 @@ package net.corda.node.utilities
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.serialization.threadLocalStorageKryo
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.*
@ -64,11 +65,11 @@ fun bytesToBlob(value: SerializedBytes<*>, finalizables: MutableList<() -> Unit>
return blob return blob
} }
fun serializeToBlob(value: Any, finalizables: MutableList<() -> Unit>): Blob = bytesToBlob(value.serialize(), finalizables) fun serializeToBlob(value: Any, finalizables: MutableList<() -> Unit>): Blob = bytesToBlob(value.serialize(threadLocalStorageKryo(), true), finalizables)
fun <T : Any> bytesFromBlob(blob: Blob): SerializedBytes<T> { fun <T : Any> bytesFromBlob(blob: Blob): SerializedBytes<T> {
try { try {
return SerializedBytes(blob.getBytes(0, blob.length().toInt())) return SerializedBytes(blob.getBytes(0, blob.length().toInt()), true)
} finally { } finally {
blob.free() blob.free()
} }

View File

@ -1,5 +1,6 @@
package net.corda.node.utilities.registration package net.corda.node.utilities.registration
import net.corda.core.serialization.CordaSerializable
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import java.security.cert.Certificate import java.security.cert.Certificate
@ -12,4 +13,5 @@ interface NetworkRegistrationService {
fun retrieveCertificates(requestId: String): Array<Certificate>? fun retrieveCertificates(requestId: String): Array<Certificate>?
} }
@CordaSerializable
class CertificateRequestException(message: String) : Exception(message) class CertificateRequestException(message: String) : Exception(message)

View File

@ -1,3 +1,4 @@
# Register a ServiceLoader service extending from net.corda.core.node.CordaPluginRegistry # Register a ServiceLoader service extending from net.corda.core.node.CordaPluginRegistry
net.corda.node.services.NotaryChange$Plugin net.corda.node.services.NotaryChange$Plugin
net.corda.node.services.persistence.DataVending$Plugin net.corda.node.services.persistence.DataVending$Plugin
net.corda.node.serialization.DefaultWhitelist

View File

@ -12,10 +12,10 @@ import net.corda.core.node.recordTransactions
import net.corda.core.node.services.VaultService import net.corda.core.node.services.VaultService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.DUMMY_NOTARY import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.node.services.vault.NodeVaultService
import net.corda.node.services.events.NodeSchedulerService import net.corda.node.services.events.NodeSchedulerService
import net.corda.node.services.persistence.DBCheckpointStorage import net.corda.node.services.persistence.DBCheckpointStorage
import net.corda.node.services.statemachine.StateMachineManager import net.corda.node.services.statemachine.StateMachineManager
import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.AddOrRemove import net.corda.node.utilities.AddOrRemove
import net.corda.node.utilities.AffinityExecutor import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase

View File

@ -10,7 +10,6 @@ import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceInfo
import net.corda.core.node.services.linearHeadsOfType import net.corda.core.node.services.linearHeadsOfType
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.flows.FinalityFlow import net.corda.flows.FinalityFlow
import net.corda.node.services.network.NetworkMapService import net.corda.node.services.network.NetworkMapService
import net.corda.node.services.transactions.ValidatingNotaryService import net.corda.node.services.transactions.ValidatingNotaryService

View File

@ -8,6 +8,7 @@ import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.irs.flows.FixingFlow import net.corda.irs.flows.FixingFlow
import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow
@ -22,6 +23,7 @@ import java.util.*
val IRS_PROGRAM_ID = InterestRateSwap() val IRS_PROGRAM_ID = InterestRateSwap()
// This is a placeholder for some types that we haven't identified exactly what they are just yet for things still in discussion // This is a placeholder for some types that we haven't identified exactly what they are just yet for things still in discussion
@CordaSerializable
open class UnknownType() { open class UnknownType() {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -106,6 +108,7 @@ abstract class RatePaymentEvent(date: LocalDate,
* Basic class for the Fixed Rate Payments on the fixed leg - see [RatePaymentEvent]. * Basic class for the Fixed Rate Payments on the fixed leg - see [RatePaymentEvent].
* Assumes that the rate is valid. * Assumes that the rate is valid.
*/ */
@CordaSerializable
class FixedRatePaymentEvent(date: LocalDate, class FixedRatePaymentEvent(date: LocalDate,
accrualStartDate: LocalDate, accrualStartDate: LocalDate,
accrualEndDate: LocalDate, accrualEndDate: LocalDate,
@ -128,6 +131,7 @@ class FixedRatePaymentEvent(date: LocalDate,
* Basic class for the Floating Rate Payments on the floating leg - see [RatePaymentEvent]. * Basic class for the Floating Rate Payments on the floating leg - see [RatePaymentEvent].
* If the rate is null returns a zero payment. // TODO: Is this the desired behaviour? * If the rate is null returns a zero payment. // TODO: Is this the desired behaviour?
*/ */
@CordaSerializable
class FloatingRatePaymentEvent(date: LocalDate, class FloatingRatePaymentEvent(date: LocalDate,
accrualStartDate: LocalDate, accrualStartDate: LocalDate,
accrualEndDate: LocalDate, accrualEndDate: LocalDate,
@ -197,6 +201,7 @@ class InterestRateSwap() : Contract {
/** /**
* This Common area contains all the information that is not leg specific. * This Common area contains all the information that is not leg specific.
*/ */
@CordaSerializable
data class Common( data class Common(
val baseCurrency: Currency, val baseCurrency: Currency,
val eligibleCurrency: Currency, val eligibleCurrency: Currency,
@ -222,6 +227,7 @@ class InterestRateSwap() : Contract {
* data that will changed from state to state (Recall that the design insists that everything is immutable, so we actually * data that will changed from state to state (Recall that the design insists that everything is immutable, so we actually
* copy / update for each transition). * copy / update for each transition).
*/ */
@CordaSerializable
data class Calculation( data class Calculation(
val expression: Expression, val expression: Expression,
val floatingLegPaymentSchedule: Map<LocalDate, FloatingRatePaymentEvent>, val floatingLegPaymentSchedule: Map<LocalDate, FloatingRatePaymentEvent>,
@ -304,6 +310,7 @@ class InterestRateSwap() : Contract {
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment) dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment)
} }
@CordaSerializable
open class FixedLeg( open class FixedLeg(
var fixedRatePayer: AnonymousParty, var fixedRatePayer: AnonymousParty,
notional: Amount<Currency>, notional: Amount<Currency>,
@ -365,6 +372,7 @@ class InterestRateSwap() : Contract {
} }
@CordaSerializable
open class FloatingLeg( open class FloatingLeg(
var floatingRatePayer: AnonymousParty, var floatingRatePayer: AnonymousParty,
notional: Amount<Currency>, notional: Amount<Currency>,

View File

@ -2,6 +2,7 @@ package net.corda.irs.contract
import net.corda.core.contracts.Amount import net.corda.core.contracts.Amount
import net.corda.core.contracts.Tenor import net.corda.core.contracts.Tenor
import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal import java.math.BigDecimal
import java.util.* import java.util.*
@ -11,6 +12,7 @@ import java.util.*
/** /**
* A utility class to prevent the various mixups between percentages, decimals, bips etc. * A utility class to prevent the various mixups between percentages, decimals, bips etc.
*/ */
@CordaSerializable
open class RatioUnit(val value: BigDecimal) { // TODO: Discuss this type open class RatioUnit(val value: BigDecimal) { // TODO: Discuss this type
override fun equals(other: Any?) = (other as? RatioUnit)?.value == value override fun equals(other: Any?) = (other as? RatioUnit)?.value == value
override fun hashCode() = value.hashCode() override fun hashCode() = value.hashCode()
@ -59,6 +61,7 @@ open class Rate(val ratioUnit: RatioUnit? = null) {
/** /**
* A very basic subclass to represent a fixed rate. * A very basic subclass to represent a fixed rate.
*/ */
@CordaSerializable
class FixedRate(ratioUnit: RatioUnit) : Rate(ratioUnit) { class FixedRate(ratioUnit: RatioUnit) : Rate(ratioUnit) {
fun isPositive(): Boolean = ratioUnit!!.value > BigDecimal("0.0") fun isPositive(): Boolean = ratioUnit!!.value > BigDecimal("0.0")
@ -69,6 +72,7 @@ class FixedRate(ratioUnit: RatioUnit) : Rate(ratioUnit) {
/** /**
* The parent class of the Floating rate classes. * The parent class of the Floating rate classes.
*/ */
@CordaSerializable
open class FloatingRate : Rate(null) open class FloatingRate : Rate(null)
/** /**

View File

@ -10,6 +10,7 @@ import net.corda.core.node.NodeInfo
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.node.services.ServiceType import net.corda.core.node.services.ServiceType
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
@ -121,6 +122,7 @@ object FixingFlow {
/** Used to set up the session between [Floater] and [Fixer] */ /** Used to set up the session between [Floater] and [Fixer] */
@CordaSerializable
data class FixingSession(val ref: StateRef, val oracleType: ServiceType) data class FixingSession(val ref: StateRef, val oracleType: ServiceType)
/** /**

View File

@ -5,8 +5,8 @@ import net.corda.core.contracts.Fix
import net.corda.core.contracts.FixOf import net.corda.core.contracts.FixOf
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.SecureHash
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.FilteredTransaction import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
@ -42,9 +42,13 @@ open class RatesFixFlow(protected val tx: TransactionBuilder,
fun tracker(fixName: String) = ProgressTracker(QUERYING(fixName), WORKING, SIGNING) fun tracker(fixName: String) = ProgressTracker(QUERYING(fixName), WORKING, SIGNING)
} }
@CordaSerializable
class FixOutOfRange(@Suppress("unused") val byAmount: BigDecimal) : Exception("Fix out of range by $byAmount") class FixOutOfRange(@Suppress("unused") val byAmount: BigDecimal) : Exception("Fix out of range by $byAmount")
@CordaSerializable
data class QueryRequest(val queries: List<FixOf>, val deadline: Instant) data class QueryRequest(val queries: List<FixOf>, val deadline: Instant)
@CordaSerializable
data class SignRequest(val ftx: FilteredTransaction) data class SignRequest(val ftx: FilteredTransaction)
// DOCSTART 2 // DOCSTART 2

View File

@ -6,6 +6,7 @@ import net.corda.core.flows.FlowLogic
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.node.utilities.TestClock import net.corda.node.utilities.TestClock
@ -20,6 +21,7 @@ object UpdateBusinessDayFlow {
// This is not really a HandshakeMessage but needs to be so that the send uses the default session ID. This will // This is not really a HandshakeMessage but needs to be so that the send uses the default session ID. This will
// resolve itself when the flow session stuff is done. // resolve itself when the flow session stuff is done.
@CordaSerializable
data class UpdateBusinessDayMessage(val date: LocalDate) data class UpdateBusinessDayMessage(val date: LocalDate)
class Plugin : CordaPluginRegistry() { class Plugin : CordaPluginRegistry() {

View File

@ -1,19 +1,16 @@
package net.corda.irs.plugin package net.corda.irs.plugin
import com.esotericsoftware.kryo.Kryo import net.corda.core.contracts.StateRef
import net.corda.core.contracts.*
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.node.CordaPluginRegistry import net.corda.core.node.CordaPluginRegistry
import net.corda.irs.api.InterestRateSwapAPI import net.corda.irs.api.InterestRateSwapAPI
import net.corda.irs.contract.* import net.corda.irs.contract.InterestRateSwap
import net.corda.irs.flows.AutoOfferFlow import net.corda.irs.flows.AutoOfferFlow
import net.corda.irs.flows.ExitServerFlow import net.corda.irs.flows.ExitServerFlow
import net.corda.irs.flows.FixingFlow import net.corda.irs.flows.FixingFlow
import net.corda.irs.flows.UpdateBusinessDayFlow import net.corda.irs.flows.UpdateBusinessDayFlow
import java.math.BigDecimal
import java.time.Duration import java.time.Duration
import java.time.LocalDate import java.time.LocalDate
import java.util.*
import java.util.function.Function import java.util.function.Function
class IRSPlugin : CordaPluginRegistry() { class IRSPlugin : CordaPluginRegistry() {
@ -28,39 +25,4 @@ class IRSPlugin : CordaPluginRegistry() {
ExitServerFlow.Broadcast::class.java.name to setOf(kotlin.Int::class.java.name), ExitServerFlow.Broadcast::class.java.name to setOf(kotlin.Int::class.java.name),
FixingFlow.FixingRoleDecider::class.java.name to setOf(StateRef::class.java.name, Duration::class.java.name), FixingFlow.FixingRoleDecider::class.java.name to setOf(StateRef::class.java.name, Duration::class.java.name),
FixingFlow.Floater::class.java.name to setOf(Party::class.java.name, FixingFlow.FixingSession::class.java.name)) FixingFlow.Floater::class.java.name to setOf(Party::class.java.name, FixingFlow.FixingSession::class.java.name))
override fun registerRPCKryoTypes(kryo: Kryo): Boolean {
kryo.apply {
register(InterestRateSwap::class.java)
register(InterestRateSwap.State::class.java)
register(InterestRateSwap.FixedLeg::class.java)
register(InterestRateSwap.FloatingLeg::class.java)
register(InterestRateSwap.Calculation::class.java)
register(InterestRateSwap.Common::class.java)
register(Expression::class.java)
register(HashMap::class.java)
register(LinkedHashMap::class.java)
register(RatioUnit::class.java)
register(Tenor::class.java)
register(Tenor.TimeUnit::class.java)
register(BusinessCalendar::class.java)
register(Comparable::class.java)
register(ReferenceRate::class.java)
register(UnknownType::class.java)
register(DayCountBasisDay::class.java)
register(DayCountBasisYear::class.java)
register(FixedRate::class.java)
register(PercentageRatioUnit::class.java)
register(BigDecimal::class.java)
register(AccrualAdjustment::class.java)
register(Frequency::class.java)
register(PaymentRule::class.java)
register(DateRollConvention::class.java)
register(LocalDate::class.java)
register(FixingFlow.FixingSession::class.java)
register(FixedRatePaymentEvent::class.java)
register(FloatingRatePaymentEvent::class.java)
}
return true
}
} }

View File

@ -1,6 +1,6 @@
{ {
"fixedLeg": { "fixedLeg": {
"fixedRatePayer": "bzs7kfAFKFTtGhxNHeN7eiqufP9Q3p9hDvSTi8AyoRAwiLK8ZZ", "fixedRatePayer": "2eFzn8gRQq7nNgypMCjKik4w8i565TM3xBmp85eefhG1c24VSj5",
"notional": { "notional": {
"quantity": 2500000000, "quantity": 2500000000,
"token": "USD" "token": "USD"
@ -25,7 +25,7 @@
"interestPeriodAdjustment": "Adjusted" "interestPeriodAdjustment": "Adjusted"
}, },
"floatingLeg": { "floatingLeg": {
"floatingRatePayer": "bzs7kf3Zc6J8mgNyH2ZddNRp3wzQt8MnPMT4zMYWgouHB4Uro5", "floatingRatePayer": "2eFzn8gJj7xcdBxExC7XEiiX36dw6HfG3MCpjMt2CaejwUnfAxb",
"notional": { "notional": {
"quantity": 2500000000, "quantity": 2500000000,
"token": "USD" "token": "USD"

View File

@ -5,6 +5,7 @@ import net.corda.core.crypto.AnonymousParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.vega.flows.SimmRevaluation import net.corda.vega.flows.SimmRevaluation
import java.security.PublicKey import java.security.PublicKey
@ -23,6 +24,7 @@ data class PortfolioState(val portfolio: List<StateRef>,
val valuation: PortfolioValuation? = null, val valuation: PortfolioValuation? = null,
override val linearId: UniqueIdentifier = UniqueIdentifier()) override val linearId: UniqueIdentifier = UniqueIdentifier())
: RevisionedState<PortfolioState.Update>, SchedulableState, DealState { : RevisionedState<PortfolioState.Update>, SchedulableState, DealState {
@CordaSerializable
data class Update(val portfolio: List<StateRef>? = null, val valuation: PortfolioValuation? = null) data class Update(val portfolio: List<StateRef>? = null, val valuation: PortfolioValuation? = null)
override val parties: List<AnonymousParty> get() = _parties.toList() override val parties: List<AnonymousParty> get() = _parties.toList()

View File

@ -2,6 +2,7 @@ package net.corda.vega.contracts
import com.opengamma.strata.basics.currency.MultiCurrencyAmount import com.opengamma.strata.basics.currency.MultiCurrencyAmount
import com.opengamma.strata.market.param.CurrencyParameterSensitivities import com.opengamma.strata.market.param.CurrencyParameterSensitivities
import net.corda.core.serialization.CordaSerializable
import net.corda.vega.analytics.CordaMarketData import net.corda.vega.analytics.CordaMarketData
import net.corda.vega.analytics.InitialMarginTriple import net.corda.vega.analytics.InitialMarginTriple
import java.math.BigDecimal import java.math.BigDecimal
@ -13,6 +14,7 @@ import java.math.BigDecimal
* We have to store trade counts in this object because a history is required and * We have to store trade counts in this object because a history is required and
* we want to avoid walking the transaction chain. * we want to avoid walking the transaction chain.
*/ */
@CordaSerializable
data class PortfolioValuation(val trades: Int, data class PortfolioValuation(val trades: Int,
val notional: BigDecimal, val notional: BigDecimal,
val marketData: CordaMarketData, val marketData: CordaMarketData,

View File

@ -11,7 +11,7 @@ import com.opengamma.strata.product.swap.type.FixedIborSwapConvention
import com.opengamma.strata.product.swap.type.FixedIborSwapConventions import com.opengamma.strata.product.swap.type.FixedIborSwapConventions
import net.corda.core.crypto.AbstractParty import net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.CompositeKey import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal import java.math.BigDecimal
import java.time.LocalDate import java.time.LocalDate
@ -36,6 +36,7 @@ data class FloatingLeg(val _notional: BigDecimal, override val notional: BigDeci
/** /**
* Represents a swap between two parties, a buyer and a seller. This class is a builder for OpenGamma SwapTrades. * Represents a swap between two parties, a buyer and a seller. This class is a builder for OpenGamma SwapTrades.
*/ */
@CordaSerializable
data class SwapData( data class SwapData(
val id: Pair<String, String>, val id: Pair<String, String>,
val buyer: Pair<String, CompositeKey>, val buyer: Pair<String, CompositeKey>,

View File

@ -4,6 +4,7 @@ import co.paralleluniverse.fibers.Suspendable
import net.corda.core.crypto.Party import net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.node.PluginServiceHub import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import net.corda.flows.TwoPartyDealFlow import net.corda.flows.TwoPartyDealFlow
@ -12,6 +13,7 @@ import net.corda.vega.contracts.OGTrade
import net.corda.vega.contracts.SwapData import net.corda.vega.contracts.SwapData
object IRSTradeFlow { object IRSTradeFlow {
@CordaSerializable
data class OfferMessage(val notary: Party, val dealBeingOffered: IRSState) data class OfferMessage(val notary: Party, val dealBeingOffered: IRSState)
class Requester(val swap: SwapData, val otherParty: Party) : FlowLogic<SignedTransaction>() { class Requester(val swap: SwapData, val otherParty: Party) : FlowLogic<SignedTransaction>() {

Some files were not shown because too many files have changed in this diff Show More