mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Kryo serialisation whitelisting and misc enhancements. (#267)
Kryo serialisation whitelisting and misc enhancements
This commit is contained in:
parent
3d04c91e61
commit
c4c4c51d7d
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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()
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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>,
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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(),
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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:
|
||||
|
@ -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 }
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -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()
|
||||
|
@ -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())
|
||||
|
@ -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.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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<*>>,
|
||||
|
@ -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,
|
||||
|
@ -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()}"
|
||||
|
@ -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
|
||||
|
@ -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>()
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -15,7 +15,7 @@ class SerializationTokenTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
kryo = THREAD_LOCAL_KRYO.get()
|
||||
kryo = threadLocalStorageKryo()
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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`.
|
||||
|
||||
|
@ -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
|
||||
--------
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
------------------
|
||||
|
||||
|
@ -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.
|
@ -70,6 +70,7 @@ Documentation Contents:
|
||||
:maxdepth: 2
|
||||
:caption: The Corda node
|
||||
|
||||
serialization
|
||||
clientrpc
|
||||
messaging
|
||||
persistence
|
||||
|
@ -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
|
||||
-----------
|
||||
|
39
docs/source/serialization.rst
Normal file
39
docs/source/serialization.rst
Normal 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`.
|
@ -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
|
||||
--------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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>()
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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?"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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> {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>,
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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>,
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user