Deprecate existing timestamp infrastructure

This commit is contained in:
Ross Nicoll 2016-07-13 10:26:17 +01:00
parent 8c7de8a69c
commit f0aa5a30d4
7 changed files with 55 additions and 14 deletions

View File

@ -403,7 +403,10 @@ class Obligation<P> : Contract {
if (input is State<P>) {
val actualOutput = outputs[stateIdx]
val deadline = input.dueBefore
val timestamp: TimestampCommand? = tx.timestamp
val timestamp: TimestampCommand? = if (tx.inputNotary == null)
null
else
tx.getTimestampBy(tx.inputNotary!!)
val expectedOutput = input.copy(lifecycle = expectedOutputLifecycle)
requireThat {

View File

@ -85,6 +85,7 @@ fun <C : CommandData> Collection<AuthenticatedObject<CommandData>>.requireSingle
mapNotNull { @Suppress("UNCHECKED_CAST") if (klass.isInstance(it.value)) it as AuthenticatedObject<C> else null }.single()
/** Returns a timestamp that was signed by the given authority, or returns null if missing. */
@Deprecated("Get timestamp from the transaction")
fun List<AuthenticatedObject<CommandData>>.getTimestampBy(timestampingAuthority: Party): TimestampCommand? {
val timestampCmds = filter { it.signers.contains(timestampingAuthority.owningKey) && it.value is TimestampCommand }
return timestampCmds.singleOrNull()?.value as? TimestampCommand

View File

@ -337,13 +337,30 @@ data class AuthenticatedObject<out T : Any>(
val value: T
)
/**
* If present in a transaction, contains a time that was verified by the uniqueness service. The true time must be
* between (after, before).
*/
data class Timestamp(val after: Instant?, val before: Instant?) {
init {
if (after == null && before == null)
throw IllegalArgumentException("At least one of before/after must be specified")
if (after != null && before != null)
check(after <= before)
}
constructor(time: Instant, tolerance: Duration) : this(time - tolerance, time + tolerance)
val midpoint: Instant get() = after!! + Duration.between(after, before!!).dividedBy(2)
}
/**
* If present in a transaction, contains a time that was verified by the timestamping authority/authorities whose
* public keys are identified in the containing [Command] object. The true time must be between (after, before).
*
* @deprecated timestamps are now a field on a transaction, and this exists just for legacy reasons.
*/
// TODO: Timestamps are now always provided by the consensus service for the transaction, rather than potentially
// having multiple timestamps on a transaction. As such, it likely makes more sense for time to be a field on the
// transaction, rather than a command
@Deprecated("timestamps are now a field on a transaction, and this exists just for legacy reasons.")
data class TimestampCommand(val after: Instant?, val before: Instant?) : CommandData {
init {
if (after == null && before == null)

View File

@ -27,8 +27,10 @@ open class TransactionBuilder(
protected val attachments: MutableList<SecureHash> = arrayListOf(),
protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(),
protected val commands: MutableList<Command> = arrayListOf(),
protected val signers: MutableSet<PublicKey> = mutableSetOf()) {
protected val signers: MutableSet<PublicKey> = mutableSetOf(),
protected var timestamp: Timestamp? = null) {
@Deprecated("use timestamp instead")
val time: TimestampCommand? get() = commands.mapNotNull { it.value as? TimestampCommand }.singleOrNull()
/**
@ -57,12 +59,30 @@ open class TransactionBuilder(
* collaborating parties may therefore require a higher time tolerance than a transaction being built by a single
* node.
*/
@Deprecated("use setTime(Instant, Duration) instead")
fun setTime(time: Instant, authority: Party, timeTolerance: Duration) {
check(currentSigs.isEmpty()) { "Cannot change timestamp after signing" }
commands.removeAll { it.value is TimestampCommand }
addCommand(TimestampCommand(time, timeTolerance), authority.owningKey)
}
/**
* Places a [TimestampCommand] in this transaction, removing any existing command if there is one.
* The command requires a signature from the Notary service, which acts as a Timestamp Authority.
* The signature can be obtained using [NotaryProtocol].
*
* The window of time in which the final timestamp may lie is defined as [time] +/- [timeTolerance].
* If you want a non-symmetrical time window you must add the command via [addCommand] yourself. The tolerance
* should be chosen such that your code can finish building the transaction and sending it to the TSA within that
* window of time, taking into account factors such as network latency. Transactions being built by a group of
* collaborating parties may therefore require a higher time tolerance than a transaction being built by a single
* node.
*/
fun setTime(time: Instant, timeTolerance: Duration) {
check(currentSigs.isEmpty()) { "Cannot change timestamp after signing" }
timestamp = Timestamp(time, timeTolerance)
}
/** A more convenient way to add items to this transaction that calls the add* methods for you based on type */
fun withItems(vararg items: Any): TransactionBuilder {
for (t in items) {
@ -115,7 +135,7 @@ open class TransactionBuilder(
}
fun toWireTransaction() = WireTransaction(ArrayList(inputs), ArrayList(attachments),
ArrayList(outputs), ArrayList(commands), signers.toList(), type)
ArrayList(outputs), ArrayList(commands), signers.toList(), type, timestamp)
fun toSignedTransaction(checkSufficientSignatures: Boolean = true): SignedTransaction {
if (checkSufficientSignatures) {

View File

@ -23,7 +23,7 @@ fun WireTransaction.toLedgerTransaction(services: ServiceHub): LedgerTransaction
services.storageService.attachments.openAttachment(it) ?: throw FileNotFoundException(it.toString())
}
val resolvedInputs = inputs.map { StateAndRef(services.loadState(it), it) }
return LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, signers, type)
return LedgerTransaction(resolvedInputs, outputs, authenticatedArgs, attachments, id, signers, type, timestamp)
}
/**

View File

@ -17,7 +17,8 @@ data class TransactionForContract(val inputs: List<ContractState>,
val attachments: List<Attachment>,
val commands: List<AuthenticatedObject<CommandData>>,
val origHash: SecureHash,
val inputNotary: Party? = null) {
val inputNotary: Party? = null,
val timestamp: Timestamp? = null) {
override fun hashCode() = origHash.hashCode()
override fun equals(other: Any?) = other is TransactionForContract && other.origHash == origHash
@ -83,11 +84,8 @@ data class TransactionForContract(val inputs: List<ContractState>,
*/
data class InOutGroup<out T : ContractState, out K : Any>(val inputs: List<T>, val outputs: List<T>, val groupingKey: K)
/** Get the timestamp command for this transaction, using the notary from the input states. */
val timestamp: TimestampCommand?
get() = if (inputNotary == null) null else commands.getTimestampBy(inputNotary)
/** Simply calls [commands.getTimestampBy] as a shortcut to make code completion more intuitive. */
@Deprecated("use timestamp property instead")
fun getTimestampBy(timestampingAuthority: Party): TimestampCommand? = commands.getTimestampBy(timestampingAuthority)
/** Simply calls [commands.getTimestampByName] as a shortcut to make code completion more intuitive. */

View File

@ -49,7 +49,8 @@ data class WireTransaction(val inputs: List<StateRef>,
val outputs: List<TransactionState<ContractState>>,
val commands: List<Command>,
val signers: List<PublicKey>,
val type: TransactionType) : NamedByHash {
val type: TransactionType,
val timestamp: Timestamp? = null) : NamedByHash {
// 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
@ -165,7 +166,8 @@ data class LedgerTransaction(
override val id: SecureHash,
/** The notary key and the command keys together: a signed transaction must provide signatures for all of these. */
val signers: List<PublicKey>,
val type: TransactionType
val type: TransactionType,
val timestamp: Timestamp? = null
) : NamedByHash {
@Suppress("UNCHECKED_CAST")
fun <T : ContractState> outRef(index: Int) = StateAndRef(outputs[index] as TransactionState<T>, StateRef(id, index))