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 kotlinx.support.jdk7.use
import net.corda.core.crypto.newSecureRandom
import net.corda.core.serialization.CordaSerializable
import org.slf4j.Logger
import rx.Observable
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.
*/
@CordaSerializable
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)
/** Representation of an operation that may have thrown an error. */
@CordaSerializable
data class ErrorOr<out A> private constructor(val value: A?, val error: Throwable?) {
// The ErrorOr holds a value iff error == 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.JsonSerialize
import com.google.common.annotations.VisibleForTesting
import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal
import java.math.BigInteger
import java.time.DayOfWeek
@ -34,6 +35,7 @@ import java.util.*
*
* @param T the type of the token, for example [Currency].
*/
@CordaSerializable
data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
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) */
@CordaSerializable
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. */
data class Fix(val of: FixOf, val value: BigDecimal) : CommandData
/** Represents a textual expression of e.g. a formula */
@CordaSerializable
@JsonDeserialize(using = ExpressionDeserializer::class)
@JsonSerialize(using = ExpressionSerializer::class)
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 */
@CordaSerializable
data class Tenor(val name: String) {
private val amount: Int
private val unit: TimeUnit
@ -166,6 +171,7 @@ data class Tenor(val name: String) {
override fun toString(): String = name
@CordaSerializable
enum class TimeUnit(val code: String) {
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.
* We don't actually do anything with this yet though, so it's ignored for now.
*/
@CordaSerializable
enum class AccrualAdjustment {
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
* finding a business day.
*/
@CordaSerializable
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.
* There are some additional rules which are explained in the individual cases below.
*/
@CordaSerializable
enum class DateRollConvention {
// 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
* in the toString lest some people get confused.
*/
@CordaSerializable
enum class DayCountBasisDay {
// We have to prefix 30 etc with a letter due to enum naming constraints.
D30,
@ -246,6 +256,7 @@ enum class DayCountBasisDay {
}
/** This forms the year part of the "Day Count Basis" used for interest calculation. */
@CordaSerializable
enum class DayCountBasisYear {
// Ditto above comment for years.
Y360,
@ -257,6 +268,7 @@ enum class DayCountBasisYear {
}
/** Whether the payment should be made before the due date, or after it. */
@CordaSerializable
enum class PaymentRule {
InAdvance, InArrears,
}
@ -266,6 +278,7 @@ enum class PaymentRule {
* 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.
@CordaSerializable
enum class Frequency(val annualCompoundCount: Int) {
Annual(1) {
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
* no staff are around to handle problems.
*/
@CordaSerializable
open class BusinessCalendar private constructor(val holidayDates: List<LocalDate>) {
@CordaSerializable
class UnknownCalendar(name: String) : Exception("$name not found")
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
* for each type of netting is left to the contract to determine.
*/
@CordaSerializable
enum class NetType {
/**
* 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
* this commodity.
*/
@CordaSerializable
data class Commodity(val commodityCode: String,
val displayName: String,
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.
* 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> {
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.FlowLogicRefFactory
import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.serialize
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
* are all free.
*/
@CordaSerializable
interface ContractState {
/**
* 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.
* 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(
/** The custom contract state */
val data: T,
@ -169,6 +172,7 @@ interface IssuanceDefinition
*
* @param P the type of product underlying the definition, for example [Currency].
*/
@CordaSerializable
data class Issued<out P>(val issuer: PartyAndReference, val product: P) {
override fun toString() = "$product issued by $issuer"
}
@ -239,6 +243,7 @@ interface LinearState : ContractState {
/**
* Standard clause to verify the LinearState safety properties.
*/
@CordaSerializable
class ClauseVerifier<S : LinearState, C : CommandData>() : Clause<S, C, Unit>() {
override fun verify(tx: TransactionForContract,
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
* transaction defined the state and where in that transaction it was.
*/
@CordaSerializable
data class StateRef(val txhash: SecureHash, val index: Int) {
override fun toString() = "$txhash($index)"
}
/** 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)
/** 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
* 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) {
constructor(party: Party, reference: OpaqueBytes) : this(party.toAnonymous(), reference)
override fun toString() = "${party}$reference"
}
/** Marker interface for classes that represent commands */
@CordaSerializable
interface CommandData
/** 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 */
@CordaSerializable
data class Command(val value: CommandData, val signers: List<CompositeKey>) {
init {
require(signers.isNotEmpty())
@ -402,6 +412,7 @@ interface NetCommand : 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. */
@CordaSerializable
data class AuthenticatedObject<out T : Any>(
val signers: List<CompositeKey>,
/** 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
* between (after, before).
*/
@CordaSerializable
data class Timestamp(val after: Instant?, val before: Instant?) {
init {
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
* 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.
*
* TODO: Contract serialization is likely to change, so the annotation is likely temporary.
*/
@CordaSerializable
interface Contract {
/**
* 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.Party
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder
/** Defines transaction build & validation logic for a specific transaction type */
@CordaSerializable
sealed class TransactionType {
override fun equals(other: Any?) = other?.javaClass == javaClass
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.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.LedgerTransaction
import java.util.*
@ -94,6 +95,7 @@ class AttachmentResolutionException(val hash : SecureHash) : FlowException() {
override fun toString(): String = "Attachment resolution failure for $hash"
}
@CordaSerializable
class TransactionConflictException(val conflictRef: StateRef, val tx1: LedgerTransaction, val tx2: LedgerTransaction) : Exception()
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) {
override val message: String get() = "Missing required encumbrance $missing in $inOut"
}
@CordaSerializable
enum class Direction {
INPUT,
OUTPUT

View File

@ -1,6 +1,7 @@
package net.corda.core.crypto
import net.corda.core.contracts.PartyAndReference
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
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
* the party. In most cases [Party] or [AnonymousParty] should be used, depending on use-case.
*/
@CordaSerializable
abstract class AbstractParty(val owningKey: CompositeKey) {
/** A helper constructor that converts the given [PublicKey] in to a [CompositeKey] with a single node */
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.Node
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
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
* create multi-level requirements such as *"either the CEO or 3 of 5 of his assistants need to sign"*.
*/
@CordaSerializable
sealed class CompositeKey {
/** Checks whether [keys] match a sufficient amount of leaf nodes */
abstract fun isFulfilledBy(keys: Iterable<PublicKey>): Boolean

View File

@ -2,6 +2,7 @@
package net.corda.core.crypto
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.i2p.crypto.eddsa.EdDSAEngine
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
* building partially signed transactions.
*/
@CordaSerializable
open class DigitalSignature(bits: ByteArray) : OpaqueBytes(bits) {
/** A digital signature that identifies who the public key is owned by. */
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)
}
@CordaSerializable
object NullPublicKey : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "NULL"
override fun getEncoded() = byteArrayOf(0)
@ -48,6 +51,7 @@ object NullPublicKey : PublicKey, Comparable<PublicKey> {
val NullCompositeKey = NullPublicKey.composite
// TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "DUMMY"
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. */
@CordaSerializable
object NullSignature : DigitalSignature.WithKey(NullPublicKey, ByteArray(32))
/** Utility to simplify the act of signing a byte array */

View File

@ -1,10 +1,10 @@
package net.corda.core.crypto
import net.corda.core.crypto.MerkleTree
import net.corda.core.crypto.SecureHash.Companion.zeroHash
import net.corda.core.serialization.CordaSerializable
import java.util.*
@CordaSerializable
class MerkleTreeException(val reason: String) : Exception() {
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).
* If both equalities hold, we can assume that l3 and l5 belong to the transaction with root h15.
*/
@CordaSerializable
class PartialMerkleTree(val root: PartialTree) {
/**
* 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
* it's easier to extract hashes used as a base for this tree.
*/
@CordaSerializable
sealed class PartialTree {
class IncludedLeaf(val hash: SecureHash) : PartialTree()
class Leaf(val hash: SecureHash) : PartialTree()

View File

@ -1,6 +1,7 @@
package net.corda.core.crypto
import com.google.common.io.BaseEncoding
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import java.security.MessageDigest
@ -8,6 +9,7 @@ import java.security.MessageDigest
* Container for a cryptographically secure hash value.
* Provides utilities for generating a cryptographic hash using different algorithms (currently only SHA-256 supported).
*/
@CordaSerializable
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) */
class SHA256(bytes: ByteArray) : SecureHash(bytes) {

View File

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

View File

@ -1,5 +1,7 @@
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.
* 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.
* 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() {
constructor(message: String?) : this(message, null)
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 net.corda.core.crypto.SecureHash
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken
import java.lang.reflect.ParameterizedType
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")
/**
@ -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].
*/
// 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?>)
/**
* 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?
*/
@CordaSerializable
data class AppContext(val attachments: List<SecureHash>) {
// TODO: build a real [AttachmentsClassLoader] etc
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.SecureHash
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.UntrustworthyData
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
* 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) {
companion object {
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.StateMachineTransactionMapping
import net.corda.core.node.services.Vault
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import rx.Observable
import java.io.InputStream
import java.io.OutputStream
import java.time.Instant
import java.util.*
@CordaSerializable
data class StateMachineInfo(
val id: StateMachineRunId,
val flowLogicClassName: String,
val progressTrackerStepAndUpdates: Pair<String, Observable<String>>?
)
@CordaSerializable
sealed class StateMachineUpdate(val id: StateMachineRunId) {
class Added(val stateMachineInfo: StateMachineInfo) : StateMachineUpdate(stateMachineInfo.id) {
override fun toString() = "Added($id, ${stateMachineInfo.flowLogicClassName})"
}
class Removed(id: StateMachineRunId) : StateMachineUpdate(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 returnValue A [ListenableFuture] of the flow's return value.
*/
@CordaSerializable
data class FlowHandle<A>(
val id: StateMachineRunId,
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.node.services.DEFAULT_SESSION_ID
import net.corda.core.node.services.PartyInfo
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.DeserializeAsKotlinObjectDef
import net.corda.core.serialization.deserialize
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
* a session is established, use [DEFAULT_SESSION_ID].
*/
@CordaSerializable
data class TopicSession(val topic: String, val sessionID: Long = DEFAULT_SESSION_ID) {
fun isBlank() = topic.isBlank() && sessionID == DEFAULT_SESSION_ID
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
* from a recipient. Using [Unit] can be ambiguous as it is similar to [Void] and so could mean no response.
*/
@CordaSerializable
object Ack : DeserializeAsKotlinObjectDef

View File

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

View File

@ -1,7 +1,7 @@
package net.corda.core.node
import com.esotericsoftware.kryo.Kryo
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.serialization.SerializationCustomization
import java.util.function.Function
/**
@ -40,14 +40,12 @@ abstract class CordaPluginRegistry(
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
* 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.
* Optionally whitelist types for use in object serialization, as we lock down the types that can be serialized.
*
* 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.
*/
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.node.services.ServiceInfo
import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
/**
* 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
*/
@CordaSerializable
data class ServiceEntry(val info: ServiceInfo, val identity: Party)
/**
* Info about a network node that acts on behalf of some form of contract party.
*/
@CordaSerializable
data class NodeInfo(val address: SingleMessageRecipient,
val legalIdentity: Party,
var advertisedServices: List<ServiceEntry> = emptyList(),

View File

@ -1,8 +1,10 @@
package net.corda.core.node
import net.corda.core.serialization.CordaSerializable
import java.util.*
/** A latitude/longitude pair. */
@CordaSerializable
data class WorldCoordinate(val latitude: Double, val longitude: Double) {
init {
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.
* 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)
/**

View File

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

View File

@ -1,5 +1,7 @@
package net.corda.core.node.services
import net.corda.core.serialization.CordaSerializable
/**
* 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
* grouping identifier for nodes collectively running a distributed service.
*/
@CordaSerializable
data class ServiceInfo(val type: ServiceType, val name: String? = null) {
companion object {
fun parse(encoded: String): ServiceInfo {

View File

@ -1,10 +1,13 @@
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
* 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.
*/
@CordaSerializable
sealed class ServiceType(val id: String) {
init {
// Enforce:

View File

@ -3,6 +3,7 @@ package net.corda.core.node.services
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.serialization.CordaSerializable
import net.corda.core.toFuture
import net.corda.core.transactions.TransactionBuilder
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).
* Relevant means they contain at least one of our pubkeys.
*/
@CordaSerializable
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
* 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>>) {
/** 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 }

View File

@ -2,8 +2,10 @@ package net.corda.core.node.services
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.StateMachineRunId
import net.corda.core.serialization.CordaSerializable
import rx.Observable
@CordaSerializable
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.crypto.Party
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
@ -15,6 +16,7 @@ interface UniquenessProvider {
fun commit(states: List<StateRef>, txId: SecureHash, callerIdentity: Party)
/** Specifies the consuming transaction for every conflicting state */
@CordaSerializable
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
* find out where exactly they were spent.
*/
@CordaSerializable
data class ConsumingTx(val id: SecureHash, val inputIndex: Int, val requestingParty: Party)
}
@CordaSerializable
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
* functionality to Java, but it won't arrive for a few years yet!
*/
@CordaSerializable
open class OpaqueBytes(val bytes: ByteArray) {
init {
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
import co.paralleluniverse.fibers.Fiber
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.*
import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.serializers.JavaSerializer
import com.esotericsoftware.kryo.serializers.MapSerializer
import de.javakaffee.kryoserializers.ArraysAsListSerializer
import de.javakaffee.kryoserializers.UnmodifiableCollectionsSerializer
import de.javakaffee.kryoserializers.guava.*
import com.esotericsoftware.kryo.util.MapReferenceResolver
import net.corda.core.contracts.*
import net.corda.core.crypto.*
import net.corda.core.node.AttachmentsClassLoader
import net.corda.core.node.services.AttachmentStorage
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 net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.objenesis.strategy.StdInstantiatorStrategy
import java.io.*
import java.lang.reflect.InvocationTargetException
import java.nio.file.Files
import java.nio.file.Path
import java.security.PublicKey
import java.time.Instant
import java.util.*
import javax.annotation.concurrent.ThreadSafe
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,
* isn't usable beyond the prototyping stage. But that's fine: we can revisit serialisation technologies later after
* 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.
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]
* 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.
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.
fun <T : Any> ByteArray.deserialize(kryo: Kryo = THREAD_LOCAL_KRYO.get()): T {
fun <T : Any> ByteArray.deserialize(kryo: Kryo = threadLocalP2PKryo()): T {
@Suppress("UNCHECKED_CAST")
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)
}
// The more specific deserialize version results in the bytes being cached, which is faster.
@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
@ -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
* 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()
Output(stream).use {
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 */
@CordaSerializable
class MissingAttachmentsException(val ids: List<SecureHash>) : Exception()
/** 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) {}
}
fun createKryo(k: Kryo = Kryo()): Kryo {
return k.apply {
// Allow any class to be deserialized (this is insecure but for prototyping we don't care)
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())
// No ClassResolver only constructor. MapReferenceResolver is the default as used by Kryo in other constructors.
fun createInternalKryo(k: Kryo = CordaKryo(makeNoWhitelistClassResolver())): Kryo {
return DefaultKryoCustomizer.customize(k)
}
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 avoid it here.
register(Kryo::class,
read = { kryo, input -> createKryo((Fiber.getFiberSerializer() as KryoSerializer).kryo) },
write = { kryo, output, obj -> }
)
/**
* We need to disable whitelist checking during calls from our Kryo code to register a serializer, since it checks
* for existing registrations and then will enter our [CordaClassResolver.getRegistration] method.
*/
open class CordaKryo(classResolver: ClassResolver) : Kryo(classResolver, MapReferenceResolver()) {
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)
register(EdDSAPrivateKey::class.java, Ed25519PrivateKeySerializer)
register(Instant::class.java, ReferencesAwareJavaSerializer)
override fun register(type: Class<*>?, id: Int): Registration {
(classResolver as? CordaClassResolver)?.disableWhitelist()
try {
return super.register(type, id)
} finally {
(classResolver as? CordaClassResolver)?.enableWhitelist()
}
}
// Using a custom serializer for compactness
register(CompositeKey.Node::class.java, CompositeKeyNodeSerializer)
register(CompositeKey.Leaf::class.java, CompositeKeyLeafSerializer)
override fun register(type: Class<*>?, serializer: Serializer<*>?): Registration {
(classResolver as? CordaClassResolver)?.disableWhitelist()
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
// constructors be invoked (typically for lazy members).
register(SignedTransaction::class.java, ImmutableClassSerializer(SignedTransaction::class))
// This class has special handling.
register(WireTransaction::class.java, WireTransactionSerializer)
// 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>()
override fun register(registration: Registration?): Registration {
(classResolver as? CordaClassResolver)?.disableWhitelist()
try {
return super.register(registration)
} finally {
(classResolver as? CordaClassResolver)?.enableWhitelist()
}
}
}

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.
*/
@CordaSerializable
interface SerializeAsToken {
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
* (when deserialized) via just the class name.
*/
@CordaSerializable
data class SingletonSerializationToken private constructor(private val className: String) : SerializationToken {
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.Party
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:
@ -16,6 +17,7 @@ import net.corda.core.crypto.SecureHash
*
* All the above refer to inputs using a (txhash, output index) pair.
*/
@CordaSerializable
class LedgerTransaction(
/** The resolved input states which will be consumed/invalidated by the execution of this transaction. */
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.crypto.*
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.createKryo
import net.corda.core.serialization.extendKryoHash
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
* field from [WireTransaction] can be used in [PartialMerkleTree] calculation.
*/
@CordaSerializable
class FilteredLeaves(
override val inputs: List<StateRef>,
override val attachments: List<SecureHash>,
@ -98,6 +100,7 @@ class FilteredLeaves(
* @param filteredLeaves Leaves included in a filtered transaction.
* @param partialMerkleTree Merkle branch needed to verify filteredLeaves.
*/
@CordaSerializable
class FilteredTransaction private constructor(
val rootHash: SecureHash,
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.signWithECDSA
import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes
import java.security.KeyPair
import java.security.SignatureException
@ -41,6 +42,7 @@ data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
*/
override val id: SecureHash get() = tx.id
@CordaSerializable
class SignaturesMissingException(val missing: Set<CompositeKey>, val descriptions: List<String>, override val id: SecureHash) : NamedByHash, SignatureException() {
override fun toString(): String {
return "Missing signatures for $descriptions on transaction ${id.prefixChars()} for ${missing.joinToString()}"

View File

@ -9,9 +9,9 @@ import net.corda.core.crypto.SecureHash
import net.corda.core.indexOfOrThrow
import net.corda.core.node.ServiceHub
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.serialize
import net.corda.core.serialization.threadLocalP2PKryo
import net.corda.core.utilities.Emoji
import java.security.PublicKey
@ -45,7 +45,7 @@ class WireTransaction(
override val id: SecureHash by lazy { merkleTree.hash }
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)
wtx.cachedBytes = data
return wtx

View File

@ -1,6 +1,7 @@
package net.corda.core.utilities
import net.corda.core.TransientProperty
import net.corda.core.serialization.CordaSerializable
import rx.Observable
import rx.Subscription
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
* using the [Observable] subscribeOn call.
*/
@CordaSerializable
class ProgressTracker(vararg steps: Step) {
@CordaSerializable
sealed class Change {
class Position(val tracker: ProgressTracker, val newStep: Step) : Change() {
override fun toString() = newStep.label
@ -48,6 +51,7 @@ class ProgressTracker(vararg steps: Step) {
}
/** The superclass of all step objects. */
@CordaSerializable
open class Step(open val label: String) {
open val changes: Observable<Change> get() = Observable.empty()
open fun childProgressTracker(): ProgressTracker? = null
@ -81,6 +85,7 @@ class ProgressTracker(vararg steps: Step) {
// This field won't be serialized.
private val _changes by TransientProperty { PublishSubject.create<Change>() }
@CordaSerializable
private data class Child(val tracker: ProgressTracker, @Transient val subscription: Subscription?)
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.FlowLogic
import net.corda.core.node.recordTransactions
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
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.
*/
@CordaSerializable
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 net.corda.core.crypto.Party
import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
@ -17,6 +18,7 @@ import net.corda.core.transactions.SignedTransaction
*/
class BroadcastTransactionFlow(val notarisedTransaction: SignedTransaction,
val participants: Set<Party>) : FlowLogic<Unit>() {
@CordaSerializable
data class NotifyTxRequest(val tx: SignedTransaction)
@Suspendable

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowException
import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.UntrustworthyData
import net.corda.core.utilities.unwrap
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 otherSide: Party) : FlowLogic<FetchDataFlow.Result<T>>() {
@CordaSerializable
class DownloadedVsRequestedDataMismatch(val requested: SecureHash, val got: SecureHash) : IllegalArgumentException()
@CordaSerializable
class DownloadedVsRequestedSizeMismatch(val requested: Int, val got: Int) : IllegalArgumentException()
class HashNotFound(val requested: SecureHash) : FlowException()
@CordaSerializable
data class Request(val hashes: List<SecureHash>)
@CordaSerializable
data class Result<out T : NamedByHash>(val fromDisk: List<T>, val downloaded: List<T>)
@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.UniquenessException
import net.corda.core.node.services.UniquenessProvider
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction
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"
}
@CordaSerializable
sealed class 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"

View File

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

View File

@ -3,10 +3,12 @@ package net.corda.flows
import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.messaging.*
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.
*/
@CordaSerializable
interface ServiceRequestMessage {
val sessionID: Long
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.services.ServiceType
import net.corda.core.seconds
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
@ -31,17 +32,21 @@ import java.security.KeyPair
*/
object TwoPartyDealFlow {
@CordaSerializable
class DealMismatchException(val expectedDeal: ContractState, val actualDeal: ContractState) : Exception() {
override fun toString() = "The submitted deal didn't match the expected: $expectedDeal vs $actualDeal"
}
@CordaSerializable
class DealRefMismatchException(val expectedDeal: StateRef, val actualDeal: StateRef) : Exception() {
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.
@CordaSerializable
data class Handshake<out T>(val payload: T, val publicKey: CompositeKey)
@CordaSerializable
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>>
}
@CordaSerializable
data class AutoOffer(val notary: Party, val dealBeingOffered: DealState)

View File

@ -207,6 +207,7 @@ class AttachmentClassLoaderTests {
val kryo = createKryo()
kryo.classLoader = cl
kryo.addToWhitelist(contract.javaClass)
val state2 = bytes.deserialize(kryo)
assert(state2.javaClass.classLoader is AttachmentsClassLoader)
@ -214,6 +215,7 @@ class AttachmentClassLoaderTests {
}
// top level wrapper
@CordaSerializable
class Data(val contract: Contract)
@Test
@ -222,7 +224,9 @@ class AttachmentClassLoaderTests {
assertNotNull(data.contract)
val bytes = data.serialize()
val kryo2 = createKryo()
kryo2.addToWhitelist(data.contract.javaClass)
val bytes = data.serialize(kryo2)
val storage = MockAttachmentStorage()
@ -234,6 +238,7 @@ class AttachmentClassLoaderTests {
val kryo = createKryo()
kryo.classLoader = cl
kryo.addToWhitelist(Class.forName("net.corda.contracts.isolated.AnotherDummyContract", true, cl))
val state2 = bytes.deserialize(kryo)
assertEquals(cl, state2.contract.javaClass.classLoader)
@ -259,6 +264,9 @@ class AttachmentClassLoaderTests {
val tx = contract.generateInitial(MEGA_CORP.ref(0), 42, DUMMY_NOTARY)
val storage = MockAttachmentStorage()
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
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())
}
@CordaSerializable
private data class Person(val name: String, val birthday: Instant?)
@Suppress("unused")
@CordaSerializable
private class Cyclic(val value: Int) {
val thisInstance = this
override fun equals(other: Any?): Boolean = (this === other) || (other is Cyclic && this.value == other.value)

View File

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

View File

@ -4,7 +4,7 @@ import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoSerializable
import com.esotericsoftware.kryo.io.Input
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 org.junit.Before
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.
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.
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
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 will currently have to register any classes or custom serialisers they require with Kryo
if they are not one of those registered by default in ``RPCKryo`` via the plugin architecture. See :doc:`creating-a-cordapp`.
This will require some familiarity with Kryo. An example is shown in :doc:`tutorial-clientrpc-api`.
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,
if they are not one of those whitelisted by default in ``DefaultWhitelist``, via either the plugin architecture or simply
with the annotation ``@CordaSerializable``. See :doc:`creating-a-cordapp` or :doc:`serialization`. 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
customisation point will either go away completely or change.
.. warning:: We will be replacing the use of Kryo in the serialization framework and so additional changes here are likely.
.. _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

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
certain conditions are met.
e. The ``registerRPCKryoTypes`` function allows custom Kryo serialisers
to be registered and whitelisted for the RPC client interface. For
instance new state types passed to flows started via RPC will need
to be explicitly registered. This will be called at various points on
various threads and needs to be stable and thread safe.
e. The ``customizeSerialization`` function allows classes to be whitelisted
for object serialisation, over and above those tagged with the ``@CordaSerializable``
annotation. In general the annotation should be preferred. For
instance new state types will need to be explicitly registered. This will be called at
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.
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:
1. Required flows: Specify which flows will be whitelisted for use in your RPC calls.
2. Service plugins: Register your services (see below).
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.
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
--------

View File

@ -1,6 +1,5 @@
package net.corda.docs
import com.esotericsoftware.kryo.Kryo
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount
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.node.CordaPluginRegistry
import net.corda.core.node.services.ServiceInfo
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.serialization.SerializationCustomization
import net.corda.core.transactions.SignedTransaction
import net.corda.flows.CashExitFlow
import net.corda.flows.CashIssueFlow
@ -132,12 +133,17 @@ fun generateTransactions(proxy: CordaRPCOps) {
// END 6
// START 7
// Not annotated, so need to whitelist manually.
data class ExampleRPCValue(val foo: String)
// Annotated, so no need to whitelist manually.
@CordaSerializable
data class ExampleRPCValue2(val bar: Int)
class ExampleRPCCordaPluginRegistry : CordaPluginRegistry() {
override fun registerRPCKryoTypes(kryo: Kryo): Boolean {
override fun customizeSerialization(custom: SerializationCustomization): Boolean {
// 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.
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.ServiceHub
import net.corda.core.node.services.unconsumedStates
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap
import net.corda.flows.FinalityFlow
@ -27,12 +28,14 @@ object FxTransactionDemoTutorial {
}
}
@CordaSerializable
private data class FxRequest(val tradeId: String,
val amount: Amount<Issued<Currency>>,
val owner: Party,
val counterparty: Party,
val notary: Party? = null)
@CordaSerializable
private data class FxResponse(val inputs: List<StateAndRef<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.ServiceHub
import net.corda.core.node.services.linearHeadsOfType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap
import net.corda.flows.FinalityFlow
@ -32,6 +33,7 @@ inline fun <reified T : LinearState> ServiceHub.latest(ref: StateRef): StateAndR
// DOCEND 1
// Minimal state model of a manual approval process
@CordaSerializable
enum class WorkflowState {
NEW,
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.
@CordaSerializable
data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>,
@ -188,6 +189,15 @@ and try again.
.. 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
------------------

View File

@ -59,6 +59,8 @@ R3
The consortium behind Corda
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.
Serialization
Object serialization is the process of converting objects into a stream of bytes and, deserialization, the reverse process.
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.
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.
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.
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
:caption: The Corda node
serialization
clientrpc
messaging
persistence

View File

@ -12,6 +12,13 @@ Milestone 9
* 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
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
-----------

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.
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
requests or responses with the `Kryo` instance RPC uses. Here's an example of how you do this for an example class.
As described in :doc:`clientrpc`, you have to whitelist any additional classes you add that are needed in RPC
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
: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`.
.. warning:: We will be replacing the use of Kryo in RPC with a stable message format and this will mean that this plugin
customisation point will either go away completely or change.
.. warning:: We will be replacing the use of Kryo in the serialization framework and so additional changes here are likely.
Security
--------

View File

@ -2,10 +2,12 @@ package net.corda.contracts.universal
import net.corda.core.contracts.Frequency
import net.corda.core.crypto.Party
import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal
import java.time.LocalDate
import java.util.*
@CordaSerializable
interface Arrangement
// 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.
data class And(val arrangements: Set<Arrangement>) : Arrangement
@CordaSerializable
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

View File

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

View File

@ -1,22 +1,34 @@
package net.corda.contracts;
import com.google.common.collect.*;
import kotlin.*;
import net.corda.contracts.asset.*;
import com.google.common.collect.ImmutableList;
import kotlin.Pair;
import kotlin.Unit;
import net.corda.contracts.asset.CashKt;
import net.corda.core.contracts.*;
import net.corda.core.contracts.TransactionForContract.*;
import net.corda.core.contracts.clauses.*;
import net.corda.core.crypto.*;
import net.corda.core.node.services.*;
import net.corda.core.transactions.*;
import org.jetbrains.annotations.*;
import net.corda.core.contracts.TransactionForContract.InOutGroup;
import net.corda.core.contracts.clauses.AnyOf;
import net.corda.core.contracts.clauses.Clause;
import net.corda.core.contracts.clauses.ClauseVerifier;
import net.corda.core.contracts.clauses.GroupClauseVerifier;
import net.corda.core.crypto.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.util.*;
import java.util.stream.*;
import java.time.Instant;
import java.util.Collections;
import java.util.Currency;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static kotlin.collections.CollectionsKt.*;
import static net.corda.core.contracts.ContractsDSL.*;
import static kotlin.collections.CollectionsKt.single;
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 {
class Move : 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.
// 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.PersistentState
import net.corda.core.schemas.QueryableState
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji
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)
}
@CordaSerializable
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.SecureHash
import net.corda.core.crypto.newSecureRandom
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import java.util.*
@ -86,6 +87,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
/**
* Standard clause for conserving the amount from input to output.
*/
@CordaSerializable
class ConserveAmount : AbstractConserveAmount<State, Commands, Commodity>()
}
@ -112,6 +114,7 @@ class CommodityContract : OnLedgerAsset<Commodity, CommodityContract.Commands, C
}
// Just for grouping
@CordaSerializable
interface Commands : FungibleAsset.Commands {
/**
* 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.crypto.*
import net.corda.core.random63BitValue
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.Emoji
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
* represented by absence of the state on transaction output.
*/
@CordaSerializable
enum class Lifecycle {
/** Default lifecycle state for a contract, in which it can be settled normally */
NORMAL,
@ -242,6 +244,7 @@ class Obligation<P> : Contract {
*
* @param P the product the obligation is for payment of.
*/
@CordaSerializable
data class Terms<P>(
/** The hash of the asset contract we're willing to accept in payment for this debt. */
val acceptableContracts: NonEmptySet<SecureHash>,
@ -323,6 +326,7 @@ class Obligation<P> : Contract {
}
// Just for grouping
@CordaSerializable
interface Commands : FungibleAsset.Commands {
/**
* 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 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.node.services.Vault
import net.corda.core.node.services.unconsumedStates

View File

@ -3,7 +3,6 @@ package net.corda.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.contracts.asset.Cash
import net.corda.core.contracts.Amount
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.TransactionType
import net.corda.core.contracts.issuedBy
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.FlowLogic
import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.OpaqueBytes
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker
@ -20,6 +21,7 @@ import java.util.*
* useful for creation of fake assets.
*/
object IssuerFlow {
@CordaSerializable
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.node.NodeInfo
import net.corda.core.seconds
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction
@ -39,17 +40,20 @@ object TwoPartyTradeFlow {
// and [AbstractStateReplacementFlow].
class UnacceptablePriceException(givenPrice: Amount<Currency>) : FlowException("Unacceptable price: $givenPrice")
class AssetMismatchException(val expectedTypeName: String, val typeName: String) : FlowException() {
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.
@CordaSerializable
data class SellerTradeInfo(
val assetForSale: StateAndRef<OwnableState>,
val price: Amount<Currency>,
val sellerOwnerKey: CompositeKey
)
@CordaSerializable
data class SignaturesFromSeller(val sellerSig: 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.node.services.DEFAULT_SESSION_ID
import net.corda.core.node.services.ServiceInfo
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.flows.ServiceRequestMessage
@ -118,6 +119,7 @@ class P2PMessagingTest : NodeBasedTest() {
return net.sendRequest<Any>(javaClass.name, request, target)
}
@CordaSerializable
private data class TestRequest(override val sessionID: Long = random63BitValue(),
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.SingleMessageRecipient
import net.corda.core.read
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.services.config.SSLConfiguration
import net.corda.node.services.messaging.ArtemisMessagingComponent.ConnectionDirection.Inbound
@ -65,6 +66,7 @@ abstract class ArtemisMessagingComponent : SingletonSerializeAsToken() {
val hostAndPort: HostAndPort
}
@CordaSerializable
data class NetworkMapAddress(override val hostAndPort: HostAndPort) : SingleMessageRecipient, ArtemisPeerAddress {
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 hostAndPort The address of the node.
*/
@CordaSerializable
data class NodeAddress(override val queueName: String, override val hostAndPort: HostAndPort) : ArtemisPeerAddress {
companion object {
fun asPeer(peerIdentity: CompositeKey, hostAndPort: HostAndPort): NodeAddress {

View File

@ -3,50 +3,22 @@
package net.corda.node.services.messaging
import com.esotericsoftware.kryo.Kryo
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.Output
import com.google.common.net.HostAndPort
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.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.toFuture
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.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.objenesis.strategy.StdInstantiatorStrategy
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import rx.Notification
import rx.Observable
import java.io.BufferedInputStream
import java.time.Instant
import java.util.*
/** Global RPC logger */
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
* method.
*/
@CordaSerializable
open class RPCException(msg: String, cause: Throwable?) : RuntimeException(msg, cause) {
constructor(msg: String) : this(msg, null)
@ -112,129 +85,20 @@ object ClassSerializer : Serializer<Class<*>>() {
}
}
@CordaSerializable
class PermissionException(msg: String) : RuntimeException(msg)
// 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,
// because we can see everything we're using in one place.
private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null) : Kryo() {
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 }
}
}
private class RPCKryo(observableSerializer: Serializer<Observable<Any>>? = null) : CordaKryo(makeStandardClassResolver()) {
init {
isRegistrationRequired = true
// 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())
DefaultKryoCustomizer.customize(this)
register(Arrays.asList("").javaClass, ArraysAsListSerializer())
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)
// RPC specific classes
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)
noReferencesWithin<WireTransaction>()
register(ErrorOr::class.java)
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

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.ServiceType
import net.corda.core.random63BitValue
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
@ -73,27 +74,33 @@ interface NetworkMapService {
override val replyTo: SingleMessageRecipient,
override val sessionID: Long = random63BitValue()) : ServiceRequestMessage
@CordaSerializable
data class FetchMapResponse(val nodes: Collection<NodeRegistration>?, val version: Int)
class QueryIdentityRequest(val identity: Party,
override val replyTo: SingleMessageRecipient,
override val sessionID: Long) : ServiceRequestMessage
@CordaSerializable
data class QueryIdentityResponse(val node: NodeInfo?)
class RegistrationRequest(val wireReg: WireNodeRegistration,
override val replyTo: SingleMessageRecipient,
override val sessionID: Long = random63BitValue()) : ServiceRequestMessage
@CordaSerializable
data class RegistrationResponse(val success: Boolean)
class SubscribeRequest(val subscribe: Boolean,
override val replyTo: SingleMessageRecipient,
override val sessionID: Long = random63BitValue()) : ServiceRequestMessage
@CordaSerializable
data class SubscribeResponse(val confirmed: Boolean)
@CordaSerializable
data class Update(val wireReg: WireNodeRegistration, val mapVersion: Int, val replyTo: MessageRecipients)
@CordaSerializable
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
// 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) {
/**
* 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.
*/
@CordaSerializable
class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalSignature.WithKey) : SignedData<NodeRegistration>(raw, sig) {
@Throws(IllegalArgumentException::class)
override fun verifyData(data: NodeRegistration) {
@ -355,6 +364,7 @@ class WireNodeRegistration(raw: SerializedBytes<NodeRegistration>, sig: DigitalS
}
}
@CordaSerializable
sealed class NodeMapError : Exception() {
/** 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()
}
@CordaSerializable
data class LastAcknowledgeInfo(val mapVersion: Int)
@CordaSerializable
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.deserialize
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.CheckpointStorage
import net.corda.node.utilities.*
@ -38,7 +39,7 @@ class DBCheckpointStorage : CheckpointStorage {
private val checkpointStorage = synchronizedMap(CheckpointMap())
override fun addCheckpoint(checkpoint: Checkpoint) {
checkpointStorage.put(checkpoint.id, checkpoint.serialize())
checkpointStorage.put(checkpoint.id, checkpoint.serialize(threadLocalStorageKryo(), true))
}
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.crypto.SecureHash
import net.corda.core.node.services.AttachmentStorage
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.loggerFor
import net.corda.node.services.api.AcceptsFileUpload
import java.io.FilterInputStream
@ -48,6 +49,7 @@ class NodeAttachmentService(val storePath: Path, metrics: MetricRegistry) : Atta
require(storePath.isDirectory()) { "$storePath must be a directory" }
}
@CordaSerializable
class OnDiskHashMismatch(val file: Path, val actual: SecureHash) : Exception() {
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.flows.FlowException
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.UntrustworthyData
@CordaSerializable
interface 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 {
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> {

View File

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

View File

@ -1,8 +1,11 @@
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.
*/
@CordaSerializable
enum class AddOrRemove {
ADD,
REMOVE

View File

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

View File

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

View File

@ -1,3 +1,4 @@
# Register a ServiceLoader service extending from net.corda.core.node.CordaPluginRegistry
net.corda.node.services.NotaryChange$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.serialization.SingletonSerializeAsToken
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.persistence.DBCheckpointStorage
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.AffinityExecutor
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.linearHeadsOfType
import net.corda.core.utilities.DUMMY_NOTARY
import net.corda.core.utilities.DUMMY_NOTARY_KEY
import net.corda.flows.FinalityFlow
import net.corda.node.services.network.NetworkMapService
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.flows.FlowLogicRefFactory
import net.corda.core.node.services.ServiceType
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import net.corda.irs.flows.FixingFlow
import net.corda.irs.utilities.suggestInterestRateAnnouncementTimeWindow
@ -22,6 +23,7 @@ import java.util.*
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
@CordaSerializable
open class UnknownType() {
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].
* Assumes that the rate is valid.
*/
@CordaSerializable
class FixedRatePaymentEvent(date: LocalDate,
accrualStartDate: LocalDate,
accrualEndDate: LocalDate,
@ -128,6 +131,7 @@ class FixedRatePaymentEvent(date: LocalDate,
* 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?
*/
@CordaSerializable
class FloatingRatePaymentEvent(date: LocalDate,
accrualStartDate: LocalDate,
accrualEndDate: LocalDate,
@ -197,6 +201,7 @@ class InterestRateSwap() : Contract {
/**
* This Common area contains all the information that is not leg specific.
*/
@CordaSerializable
data class Common(
val baseCurrency: 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
* copy / update for each transition).
*/
@CordaSerializable
data class Calculation(
val expression: Expression,
val floatingLegPaymentSchedule: Map<LocalDate, FloatingRatePaymentEvent>,
@ -304,6 +310,7 @@ class InterestRateSwap() : Contract {
dayCountBasisDay, dayCountBasisYear, dayInMonth, paymentRule, paymentDelay, paymentCalendar, interestPeriodAdjustment)
}
@CordaSerializable
open class FixedLeg(
var fixedRatePayer: AnonymousParty,
notional: Amount<Currency>,
@ -365,6 +372,7 @@ class InterestRateSwap() : Contract {
}
@CordaSerializable
open class FloatingLeg(
var floatingRatePayer: AnonymousParty,
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.Tenor
import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal
import java.util.*
@ -11,6 +12,7 @@ import java.util.*
/**
* A utility class to prevent the various mixups between percentages, decimals, bips etc.
*/
@CordaSerializable
open class RatioUnit(val value: BigDecimal) { // TODO: Discuss this type
override fun equals(other: Any?) = (other as? RatioUnit)?.value == value
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.
*/
@CordaSerializable
class FixedRate(ratioUnit: RatioUnit) : Rate(ratioUnit) {
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.
*/
@CordaSerializable
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.services.ServiceType
import net.corda.core.seconds
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.trace
@ -121,6 +122,7 @@ object FixingFlow {
/** Used to set up the session between [Floater] and [Fixer] */
@CordaSerializable
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.crypto.DigitalSignature
import net.corda.core.crypto.Party
import net.corda.core.crypto.SecureHash
import net.corda.core.flows.FlowLogic
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.FilteredTransaction
import net.corda.core.transactions.TransactionBuilder
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)
}
@CordaSerializable
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)
@CordaSerializable
data class SignRequest(val ftx: FilteredTransaction)
// 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.NodeInfo
import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.unwrap
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
// resolve itself when the flow session stuff is done.
@CordaSerializable
data class UpdateBusinessDayMessage(val date: LocalDate)
class Plugin : CordaPluginRegistry() {

View File

@ -1,19 +1,16 @@
package net.corda.irs.plugin
import com.esotericsoftware.kryo.Kryo
import net.corda.core.contracts.*
import net.corda.core.contracts.StateRef
import net.corda.core.crypto.Party
import net.corda.core.node.CordaPluginRegistry
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.ExitServerFlow
import net.corda.irs.flows.FixingFlow
import net.corda.irs.flows.UpdateBusinessDayFlow
import java.math.BigDecimal
import java.time.Duration
import java.time.LocalDate
import java.util.*
import java.util.function.Function
class IRSPlugin : CordaPluginRegistry() {
@ -28,39 +25,4 @@ class IRSPlugin : CordaPluginRegistry() {
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.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": {
"fixedRatePayer": "bzs7kfAFKFTtGhxNHeN7eiqufP9Q3p9hDvSTi8AyoRAwiLK8ZZ",
"fixedRatePayer": "2eFzn8gRQq7nNgypMCjKik4w8i565TM3xBmp85eefhG1c24VSj5",
"notional": {
"quantity": 2500000000,
"token": "USD"
@ -25,7 +25,7 @@
"interestPeriodAdjustment": "Adjusted"
},
"floatingLeg": {
"floatingRatePayer": "bzs7kf3Zc6J8mgNyH2ZddNRp3wzQt8MnPMT4zMYWgouHB4Uro5",
"floatingRatePayer": "2eFzn8gJj7xcdBxExC7XEiiX36dw6HfG3MCpjMt2CaejwUnfAxb",
"notional": {
"quantity": 2500000000,
"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.Party
import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.TransactionBuilder
import net.corda.vega.flows.SimmRevaluation
import java.security.PublicKey
@ -23,6 +24,7 @@ data class PortfolioState(val portfolio: List<StateRef>,
val valuation: PortfolioValuation? = null,
override val linearId: UniqueIdentifier = UniqueIdentifier())
: RevisionedState<PortfolioState.Update>, SchedulableState, DealState {
@CordaSerializable
data class Update(val portfolio: List<StateRef>? = null, val valuation: PortfolioValuation? = null)
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.market.param.CurrencyParameterSensitivities
import net.corda.core.serialization.CordaSerializable
import net.corda.vega.analytics.CordaMarketData
import net.corda.vega.analytics.InitialMarginTriple
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 want to avoid walking the transaction chain.
*/
@CordaSerializable
data class PortfolioValuation(val trades: Int,
val notional: BigDecimal,
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 net.corda.core.crypto.AbstractParty
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.Party
import net.corda.core.serialization.CordaSerializable
import java.math.BigDecimal
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.
*/
@CordaSerializable
data class SwapData(
val id: Pair<String, String>,
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.flows.FlowLogic
import net.corda.core.node.PluginServiceHub
import net.corda.core.serialization.CordaSerializable
import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.unwrap
import net.corda.flows.TwoPartyDealFlow
@ -12,6 +13,7 @@ import net.corda.vega.contracts.OGTrade
import net.corda.vega.contracts.SwapData
object IRSTradeFlow {
@CordaSerializable
data class OfferMessage(val notary: Party, val dealBeingOffered: IRSState)
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