Introduce a NamedByHash interface, for things that are identified via secure hash.

This commit is contained in:
Mike Hearn 2016-02-25 19:23:00 +01:00
parent dfc15a6bab
commit c24d991a7e
2 changed files with 30 additions and 5 deletions

View File

@ -12,9 +12,16 @@ import core.crypto.SecureHash
import core.crypto.toStringShort import core.crypto.toStringShort
import core.serialization.OpaqueBytes import core.serialization.OpaqueBytes
import core.serialization.serialize import core.serialization.serialize
import java.io.InputStream
import java.security.PublicKey import java.security.PublicKey
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.jar.JarInputStream
/** Implemented by anything that can be named by a secure hash value (e.g. transactions, attachments). */
interface NamedByHash {
val id: SecureHash
}
/** /**
* A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk * A contract state (or just "state") contains opaque data used by a contract program. It can be thought of as a disk
@ -147,3 +154,19 @@ interface ContractFactory {
} }
class UnknownContractException : Exception() class UnknownContractException : Exception()
/**
* An attachment is a ZIP (or an optionally signed JAR) that contains one or more files. Attachments are meant to
* contain public static data which can be referenced from transactions and utilised from contracts. Good examples
* of how attachments are meant to be used include:
*
* - Calendar data
* - Fixes (e.g. LIBOR)
* - Smart contract code
* - Legal documents
* - Facts generated by oracles which might be reused a lot
*/
interface Attachment : NamedByHash {
fun open(): InputStream
fun openAsJAR() = JarInputStream(open())
}

View File

@ -57,12 +57,13 @@ import java.util.*
/** Transaction ready for serialisation, without any signatures attached. */ /** Transaction ready for serialisation, without any signatures attached. */
data class WireTransaction(val inputs: List<StateRef>, data class WireTransaction(val inputs: List<StateRef>,
val outputs: List<ContractState>, val outputs: List<ContractState>,
val commands: List<Command>) { val commands: List<Command>) : NamedByHash {
// Cache the serialised form of the transaction and its hash to give us fast access to it. // Cache the serialised form of the transaction and its hash to give us fast access to it.
@Volatile @Transient private var cachedBits: SerializedBytes<WireTransaction>? = null @Volatile @Transient private var cachedBits: SerializedBytes<WireTransaction>? = null
val serialized: SerializedBytes<WireTransaction> get() = cachedBits ?: serialize().apply { cachedBits = this } val serialized: SerializedBytes<WireTransaction> get() = cachedBits ?: serialize().apply { cachedBits = this }
val id: SecureHash get() = serialized.hash override val id: SecureHash get() = serialized.hash
companion object { companion object {
fun deserialize(bits: SerializedBytes<WireTransaction>): WireTransaction { fun deserialize(bits: SerializedBytes<WireTransaction>): WireTransaction {
val wtx = bits.deserialize() val wtx = bits.deserialize()
@ -98,14 +99,15 @@ data class WireTransaction(val inputs: List<StateRef>,
} }
/** Container for a [WireTransaction] and attached signatures. */ /** Container for a [WireTransaction] and attached signatures. */
data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>, val sigs: List<DigitalSignature.WithKey>) { data class SignedTransaction(val txBits: SerializedBytes<WireTransaction>,
val sigs: List<DigitalSignature.WithKey>) : NamedByHash {
init { check(sigs.isNotEmpty()) } init { check(sigs.isNotEmpty()) }
/** Lazily calculated access to the deserialised/hashed transaction data. */ /** Lazily calculated access to the deserialised/hashed transaction data. */
val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) } val tx: WireTransaction by lazy { WireTransaction.deserialize(txBits) }
/** A transaction ID is the hash of the [WireTransaction]. Thus adding or removing a signature does not change it. */ /** A transaction ID is the hash of the [WireTransaction]. Thus adding or removing a signature does not change it. */
val id: SecureHash get() = txBits.hash override val id: SecureHash get() = txBits.hash
/** /**
* Verifies the given signatures against the serialized transaction data. Does NOT deserialise or check the contents * Verifies the given signatures against the serialized transaction data. Does NOT deserialise or check the contents