mirror of
https://github.com/corda/corda.git
synced 2025-05-02 16:53:22 +00:00
Moves to builder syntax for TxBuilder. Adds attachments and time-windows to withItems.
This commit is contained in:
parent
c6e165947b
commit
3063debd98
@ -147,9 +147,10 @@ sealed class TransactionType {
|
|||||||
* and adds the list of participants to the signers set for every input state.
|
* and adds the list of participants to the signers set for every input state.
|
||||||
*/
|
*/
|
||||||
class Builder(notary: Party) : TransactionBuilder(NotaryChange, notary) {
|
class Builder(notary: Party) : TransactionBuilder(NotaryChange, notary) {
|
||||||
override fun addInputState(stateAndRef: StateAndRef<*>) {
|
override fun addInputState(stateAndRef: StateAndRef<*>): TransactionBuilder {
|
||||||
signers.addAll(stateAndRef.state.data.participants.map { it.owningKey })
|
signers.addAll(stateAndRef.state.data.participants.map { it.owningKey })
|
||||||
super.addInputState(stateAndRef)
|
super.addInputState(stateAndRef)
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package net.corda.core.transactions
|
package net.corda.core.transactions
|
||||||
|
|
||||||
import co.paralleluniverse.strands.Strand
|
import co.paralleluniverse.strands.Strand
|
||||||
import com.google.common.annotations.VisibleForTesting
|
|
||||||
import net.corda.core.contracts.*
|
import net.corda.core.contracts.*
|
||||||
import net.corda.core.crypto.*
|
import net.corda.core.crypto.*
|
||||||
import net.corda.core.internal.FlowStateMachine
|
|
||||||
import net.corda.core.identity.Party
|
import net.corda.core.identity.Party
|
||||||
|
import net.corda.core.internal.FlowStateMachine
|
||||||
import net.corda.core.node.ServiceHub
|
import net.corda.core.node.ServiceHub
|
||||||
import net.corda.core.serialization.serialize
|
import net.corda.core.serialization.serialize
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@ -40,23 +39,22 @@ open class TransactionBuilder(
|
|||||||
protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(),
|
protected val outputs: MutableList<TransactionState<ContractState>> = arrayListOf(),
|
||||||
protected val commands: MutableList<Command> = arrayListOf(),
|
protected val commands: MutableList<Command> = arrayListOf(),
|
||||||
protected val signers: MutableSet<PublicKey> = mutableSetOf(),
|
protected val signers: MutableSet<PublicKey> = mutableSetOf(),
|
||||||
window: TimeWindow? = null) {
|
protected var window: TimeWindow? = null) {
|
||||||
constructor(type: TransactionType, notary: Party) : this(type, notary, (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID())
|
constructor(type: TransactionType, notary: Party) : this(type, notary, (Strand.currentStrand() as? FlowStateMachine<*>)?.id?.uuid ?: UUID.randomUUID())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a copy of the builder.
|
* Creates a copy of the builder.
|
||||||
*/
|
*/
|
||||||
fun copy(): TransactionBuilder =
|
fun copy() = TransactionBuilder(
|
||||||
TransactionBuilder(
|
type = type,
|
||||||
type = type,
|
notary = notary,
|
||||||
notary = notary,
|
inputs = ArrayList(inputs),
|
||||||
inputs = ArrayList(inputs),
|
attachments = ArrayList(attachments),
|
||||||
attachments = ArrayList(attachments),
|
outputs = ArrayList(outputs),
|
||||||
outputs = ArrayList(outputs),
|
commands = ArrayList(commands),
|
||||||
commands = ArrayList(commands),
|
signers = LinkedHashSet(signers),
|
||||||
signers = LinkedHashSet(signers),
|
window = window
|
||||||
window = timeWindow
|
)
|
||||||
)
|
|
||||||
|
|
||||||
// DOCSTART 1
|
// DOCSTART 1
|
||||||
/** A more convenient way to add items to this transaction that calls the add* methods for you based on type */
|
/** A more convenient way to add items to this transaction that calls the add* methods for you based on type */
|
||||||
@ -64,10 +62,12 @@ open class TransactionBuilder(
|
|||||||
for (t in items) {
|
for (t in items) {
|
||||||
when (t) {
|
when (t) {
|
||||||
is StateAndRef<*> -> addInputState(t)
|
is StateAndRef<*> -> addInputState(t)
|
||||||
|
is SecureHash -> addAttachment(t)
|
||||||
is TransactionState<*> -> addOutputState(t)
|
is TransactionState<*> -> addOutputState(t)
|
||||||
is ContractState -> addOutputState(t)
|
is ContractState -> addOutputState(t)
|
||||||
is Command -> addCommand(t)
|
is Command -> addCommand(t)
|
||||||
is CommandData -> throw IllegalArgumentException("You passed an instance of CommandData, but that lacks the pubkey. You need to wrap it in a Command object first.")
|
is CommandData -> throw IllegalArgumentException("You passed an instance of CommandData, but that lacks the pubkey. You need to wrap it in a Command object first.")
|
||||||
|
is TimeWindow -> setTimeWindow(t)
|
||||||
else -> throw IllegalArgumentException("Wrong argument type: ${t.javaClass}")
|
else -> throw IllegalArgumentException("Wrong argument type: ${t.javaClass}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ open class TransactionBuilder(
|
|||||||
// DOCEND 1
|
// DOCEND 1
|
||||||
|
|
||||||
fun toWireTransaction() = WireTransaction(ArrayList(inputs), ArrayList(attachments),
|
fun toWireTransaction() = WireTransaction(ArrayList(inputs), ArrayList(attachments),
|
||||||
ArrayList(outputs), ArrayList(commands), notary, signers.toList(), type, timeWindow)
|
ArrayList(outputs), ArrayList(commands), notary, signers.toList(), type, window)
|
||||||
|
|
||||||
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class)
|
@Throws(AttachmentResolutionException::class, TransactionResolutionException::class)
|
||||||
fun toLedgerTransaction(services: ServiceHub) = toWireTransaction().toLedgerTransaction(services)
|
fun toLedgerTransaction(services: ServiceHub) = toWireTransaction().toLedgerTransaction(services)
|
||||||
@ -86,51 +86,55 @@ open class TransactionBuilder(
|
|||||||
toLedgerTransaction(services).verify()
|
toLedgerTransaction(services).verify()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun addInputState(stateAndRef: StateAndRef<*>) {
|
open fun addInputState(stateAndRef: StateAndRef<*>): TransactionBuilder {
|
||||||
val notary = stateAndRef.state.notary
|
val notary = stateAndRef.state.notary
|
||||||
require(notary == this.notary) { "Input state requires notary \"$notary\" which does not match the transaction notary \"${this.notary}\"." }
|
require(notary == this.notary) { "Input state requires notary \"$notary\" which does not match the transaction notary \"${this.notary}\"." }
|
||||||
signers.add(notary.owningKey)
|
signers.add(notary.owningKey)
|
||||||
inputs.add(stateAndRef.ref)
|
inputs.add(stateAndRef.ref)
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addAttachment(attachmentId: SecureHash) {
|
fun addAttachment(attachmentId: SecureHash): TransactionBuilder {
|
||||||
attachments.add(attachmentId)
|
attachments.add(attachmentId)
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addOutputState(state: TransactionState<*>): Int {
|
fun addOutputState(state: TransactionState<*>): TransactionBuilder {
|
||||||
outputs.add(state)
|
outputs.add(state)
|
||||||
return outputs.size - 1
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun addOutputState(state: ContractState, notary: Party, encumbrance: Int? = null) = addOutputState(TransactionState(state, notary, encumbrance))
|
fun addOutputState(state: ContractState, notary: Party, encumbrance: Int? = null) = addOutputState(TransactionState(state, notary, encumbrance))
|
||||||
|
|
||||||
/** A default notary must be specified during builder construction to use this method */
|
/** A default notary must be specified during builder construction to use this method */
|
||||||
fun addOutputState(state: ContractState): Int {
|
fun addOutputState(state: ContractState): TransactionBuilder {
|
||||||
checkNotNull(notary) { "Need to specify a notary for the state, or set a default one on TransactionBuilder initialisation" }
|
checkNotNull(notary) { "Need to specify a notary for the state, or set a default one on TransactionBuilder initialisation" }
|
||||||
return addOutputState(state, notary!!)
|
addOutputState(state, notary!!)
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCommand(arg: Command) {
|
fun addCommand(arg: Command): TransactionBuilder {
|
||||||
// TODO: replace pubkeys in commands with 'pointers' to keys in signers
|
// TODO: replace pubkeys in commands with 'pointers' to keys in signers
|
||||||
signers.addAll(arg.signers)
|
signers.addAll(arg.signers)
|
||||||
commands.add(arg)
|
commands.add(arg)
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys)))
|
fun addCommand(data: CommandData, vararg keys: PublicKey) = addCommand(Command(data, listOf(*keys)))
|
||||||
fun addCommand(data: CommandData, keys: List<PublicKey>) = addCommand(Command(data, keys))
|
fun addCommand(data: CommandData, keys: List<PublicKey>) = addCommand(Command(data, keys))
|
||||||
|
|
||||||
var timeWindow: TimeWindow? = window
|
/**
|
||||||
/**
|
* Sets the [TimeWindow] for this transaction, replacing the existing [TimeWindow] if there is one. To be valid, the
|
||||||
* Sets the [TimeWindow] for this transaction, replacing the existing [TimeWindow] if there is one. To be valid, the
|
* transaction must then be signed by the notary service within this window of time. In this way, the notary acts as
|
||||||
* transaction must then be signed by the notary service within this window of time. In this way, the notary acts as
|
* the Timestamp Authority.
|
||||||
* the Timestamp Authority.
|
*/
|
||||||
*/
|
fun setTimeWindow(timeWindow: TimeWindow): TransactionBuilder {
|
||||||
set(value) {
|
check(notary != null) { "Only notarised transactions can have a time-window" }
|
||||||
check(notary != null) { "Only notarised transactions can have a time-window" }
|
signers.add(notary!!.owningKey)
|
||||||
signers.add(notary!!.owningKey)
|
window = timeWindow
|
||||||
field = value
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [TimeWindow] for the transaction can also be defined as [time] +/- [timeTolerance]. The tolerance should be
|
* The [TimeWindow] for the transaction can also be defined as [time] +/- [timeTolerance]. The tolerance should be
|
||||||
@ -139,9 +143,7 @@ open class TransactionBuilder(
|
|||||||
* collaborating parties may therefore require a higher time tolerance than a transaction being built by a single
|
* collaborating parties may therefore require a higher time tolerance than a transaction being built by a single
|
||||||
* node.
|
* node.
|
||||||
*/
|
*/
|
||||||
fun setTimeWindow(time: Instant, timeTolerance: Duration) {
|
fun setTimeWindow(time: Instant, timeTolerance: Duration) = setTimeWindow(TimeWindow.withTolerance(time, timeTolerance))
|
||||||
timeWindow = TimeWindow.withTolerance(time, timeTolerance)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accessors that yield immutable snapshots.
|
// Accessors that yield immutable snapshots.
|
||||||
fun inputStates(): List<StateRef> = ArrayList(inputs)
|
fun inputStates(): List<StateRef> = ArrayList(inputs)
|
||||||
|
@ -289,7 +289,7 @@ object FlowCookbook {
|
|||||||
regTxBuilder.addAttachment(ourAttachment)
|
regTxBuilder.addAttachment(ourAttachment)
|
||||||
|
|
||||||
// We set the time-window within which the transaction must be notarised using either of:
|
// We set the time-window within which the transaction must be notarised using either of:
|
||||||
regTxBuilder.timeWindow = ourTimeWindow
|
regTxBuilder.setTimeWindow(ourTimeWindow)
|
||||||
regTxBuilder.setTimeWindow(serviceHub.clock.instant(), Duration.ofSeconds(30))
|
regTxBuilder.setTimeWindow(serviceHub.clock.instant(), Duration.ofSeconds(30))
|
||||||
|
|
||||||
/**----------------------
|
/**----------------------
|
||||||
|
@ -116,11 +116,12 @@ data class TestTransactionDSLInterpreter private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun _output(label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) {
|
override fun _output(label: String?, notary: Party, encumbrance: Int?, contractState: ContractState) {
|
||||||
val outputIndex = transactionBuilder.addOutputState(contractState, notary, encumbrance)
|
transactionBuilder.addOutputState(contractState, notary, encumbrance)
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
if (label in labelToIndexMap) {
|
if (label in labelToIndexMap) {
|
||||||
throw DuplicateOutputLabel(label)
|
throw DuplicateOutputLabel(label)
|
||||||
} else {
|
} else {
|
||||||
|
val outputIndex = transactionBuilder.outputStates().size - 1
|
||||||
labelToIndexMap[label] = outputIndex
|
labelToIndexMap[label] = outputIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,7 +146,7 @@ data class TestTransactionDSLInterpreter private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun timeWindow(data: TimeWindow) {
|
override fun timeWindow(data: TimeWindow) {
|
||||||
transactionBuilder.timeWindow = data
|
transactionBuilder.setTimeWindow(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tweak(
|
override fun tweak(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user