mirror of
https://github.com/corda/corda.git
synced 2025-06-06 17:31:44 +00:00
testdsl: Javadoc comments
This commit is contained in:
parent
3d885eb928
commit
cd0299f650
@ -18,38 +18,6 @@ import java.security.PublicKey
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
// In a real system this would be a persistent map of hash to bytecode and we'd instantiate the object as needed inside
|
|
||||||
// a sandbox. For unit tests we just have a hard-coded list.
|
|
||||||
val TEST_PROGRAM_MAP: Map<Contract, Class<out Contract>> = mapOf(
|
|
||||||
CASH_PROGRAM_ID to Cash::class.java,
|
|
||||||
CP_PROGRAM_ID to CommercialPaper::class.java,
|
|
||||||
JavaCommercialPaper.JCP_PROGRAM_ID to JavaCommercialPaper::class.java,
|
|
||||||
DUMMY_PROGRAM_ID to DummyContract::class.java,
|
|
||||||
IRS_PROGRAM_ID to InterestRateSwap::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
fun generateState() = DummyState(Random().nextInt())
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Defines a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes.
|
|
||||||
//
|
|
||||||
// Define a transaction like this:
|
|
||||||
//
|
|
||||||
// transaction {
|
|
||||||
// input { someExpression }
|
|
||||||
// output { someExpression }
|
|
||||||
// arg { someExpression }
|
|
||||||
//
|
|
||||||
// tweak {
|
|
||||||
// ... same thing but works with a copy of the parent, can add inputs/outputs/args just within this scope.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// contract.accepts() -> should pass
|
|
||||||
// contract `fails requirement` "some substring of the error message"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// For Java compatibility please define helper methods here and then define the infix notation
|
|
||||||
object JavaTestHelpers {
|
object JavaTestHelpers {
|
||||||
@JvmStatic fun ownedBy(state: Cash.State, owner: PublicKey) = state.copy(owner = owner)
|
@JvmStatic fun ownedBy(state: Cash.State, owner: PublicKey) = state.copy(owner = owner)
|
||||||
@JvmStatic fun issuedBy(state: Cash.State, party: Party) = state.copy(amount = Amount(state.amount.quantity, state.issuanceDef.copy(issuer = state.deposit.copy(party = party))))
|
@JvmStatic fun issuedBy(state: Cash.State, party: Party) = state.copy(amount = Amount(state.amount.quantity, state.issuanceDef.copy(issuer = state.deposit.copy(party = party))))
|
||||||
|
@ -169,4 +169,4 @@ operator fun KeyPair.component1() = this.private
|
|||||||
operator fun KeyPair.component2() = this.public
|
operator fun KeyPair.component2() = this.public
|
||||||
|
|
||||||
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
|
/** A simple wrapper that will make it easier to swap out the EC algorithm we use in future */
|
||||||
fun generateKeyPair() = EddsaKeyPairGenerator().generateKeyPair()
|
fun generateKeyPair() = EddsaKeyPairGenerator().generateKeyPair()!!
|
||||||
|
@ -4,42 +4,105 @@ import com.r3corda.core.contracts.*
|
|||||||
import com.r3corda.core.crypto.SecureHash
|
import com.r3corda.core.crypto.SecureHash
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines output state lookup by label. It is split from the interpreter interfaces so that outputs may
|
||||||
|
* be looked up both in ledger{..} and transaction{..} blocks.
|
||||||
|
*/
|
||||||
interface OutputStateLookup {
|
interface OutputStateLookup {
|
||||||
|
/**
|
||||||
|
* Retrieves an output previously defined by [TransactionDSLInterpreter._output] with a label passed in.
|
||||||
|
* @param clazz: The class object holding the type of the output state expected.
|
||||||
|
* @param label: The label of the to-be-retrieved output state
|
||||||
|
* @return: The output [StateAndRef]
|
||||||
|
*/
|
||||||
fun <S : ContractState> retrieveOutputStateAndRef(clazz: Class<S>, label: String): StateAndRef<S>
|
fun <S : ContractState> retrieveOutputStateAndRef(clazz: Class<S>, label: String): StateAndRef<S>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines the bare bone functionality that a Ledger DSL interpreter should implement.
|
||||||
|
*
|
||||||
|
* TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy
|
||||||
|
*/
|
||||||
interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : OutputStateLookup {
|
interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : OutputStateLookup {
|
||||||
|
/**
|
||||||
|
* Creates and adds a transaction to the ledger.
|
||||||
|
* @param transactionLabel: Optional label of the transaction, to be used in diagnostic messages.
|
||||||
|
* @param transactionBuilder: The base transactionBuilder that will be used to build the transaction.
|
||||||
|
* @param dsl: The dsl that should be interpreted for building the transaction.
|
||||||
|
* @return: The final [WireTransaction] of the built transaction.
|
||||||
|
*/
|
||||||
fun _transaction(transactionLabel: String?, transactionBuilder: TransactionBuilder,
|
fun _transaction(transactionLabel: String?, transactionBuilder: TransactionBuilder,
|
||||||
dsl: TransactionDSL<R, T>.() -> R): WireTransaction
|
dsl: TransactionDSL<R, T>.() -> R): WireTransaction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and adds a transaction to the ledger that will not be verified by [verifies].
|
||||||
|
* @param transactionLabel: Optional label of the transaction, to be used in diagnostic messages.
|
||||||
|
* @param transactionBuilder: The base transactionBuilder that will be used to build the transaction.
|
||||||
|
* @param dsl: The dsl that should be interpreted for building the transaction.
|
||||||
|
* @return: The final [WireTransaction] of the built transaction.
|
||||||
|
*/
|
||||||
fun _unverifiedTransaction(transactionLabel: String?, transactionBuilder: TransactionBuilder,
|
fun _unverifiedTransaction(transactionLabel: String?, transactionBuilder: TransactionBuilder,
|
||||||
dsl: TransactionDSL<R, T>.() -> Unit): WireTransaction
|
dsl: TransactionDSL<R, T>.() -> Unit): WireTransaction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a local scoped copy of the ledger.
|
||||||
|
* @param dsl: The ledger DSL to be interpreted using the copy.
|
||||||
|
*/
|
||||||
fun tweak(dsl: LedgerDSL<R, T, LedgerDSLInterpreter<R, T>>.() -> Unit)
|
fun tweak(dsl: LedgerDSL<R, T, LedgerDSLInterpreter<R, T>>.() -> Unit)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an attachment to the ledger.
|
||||||
|
* @param attachment: The [InputStream] defining the contents of the attachment.
|
||||||
|
* @return: The [SecureHash] that identifies the attachment, to be used in transactions.
|
||||||
|
*/
|
||||||
fun attachment(attachment: InputStream): SecureHash
|
fun attachment(attachment: InputStream): SecureHash
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the ledger using [TransactionGroup.verify], throws if the verification fails.
|
||||||
|
*/
|
||||||
fun verifies()
|
fun verifies()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the class the top-level primitives deal with. It delegates all other primitives to the contained interpreter.
|
* This is the class that defines the syntactic sugar of the ledger Test DSL and delegates to the contained interpreter,
|
||||||
* This way we have a decoupling of the DSL "AST" and the interpretation(s) of it. Note how the delegation forces
|
* and what is actually used in `ledger { (...) }`. Add convenience functions here, or if you want to extend the DSL
|
||||||
* covariance of the TransactionInterpreter parameter.
|
* functionality then first add your primitive to [LedgerDSLInterpreter] and then add the convenience defaults/extension
|
||||||
*
|
* methods here.
|
||||||
* TODO (Kotlin 1.1): Use type synonyms to make the type params less unwieldy
|
|
||||||
*/
|
*/
|
||||||
class LedgerDSL<R, out T : TransactionDSLInterpreter<R>, out L : LedgerDSLInterpreter<R, T>> (val interpreter: L) :
|
class LedgerDSL<R, out T : TransactionDSLInterpreter<R>, out L : LedgerDSLInterpreter<R, T>> (val interpreter: L) :
|
||||||
LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>> by interpreter {
|
LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>> by interpreter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see LedgerDSLInterpreter._transaction
|
||||||
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun transaction(label: String? = null, transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
fun transaction(label: String? = null, transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
||||||
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R) =
|
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R) =
|
||||||
_transaction(label, transactionBuilder, dsl)
|
_transaction(label, transactionBuilder, dsl)
|
||||||
|
/**
|
||||||
|
* @see LedgerDSLInterpreter._unverifiedTransaction
|
||||||
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun unverifiedTransaction(label: String? = null, transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
fun unverifiedTransaction(label: String? = null, transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
||||||
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> Unit) =
|
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> Unit) =
|
||||||
_unverifiedTransaction(label, transactionBuilder, dsl)
|
_unverifiedTransaction(label, transactionBuilder, dsl)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see OutputStateLookup.retrieveOutputStateAndRef
|
||||||
|
*/
|
||||||
inline fun <reified S : ContractState> String.outputStateAndRef(): StateAndRef<S> =
|
inline fun <reified S : ContractState> String.outputStateAndRef(): StateAndRef<S> =
|
||||||
retrieveOutputStateAndRef(S::class.java, this)
|
retrieveOutputStateAndRef(S::class.java, this)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the output [TransactionState] based on the label.
|
||||||
|
* @see OutputStateLookup.retrieveOutputStateAndRef
|
||||||
|
*/
|
||||||
inline fun <reified S : ContractState> String.output(): TransactionState<S> =
|
inline fun <reified S : ContractState> String.output(): TransactionState<S> =
|
||||||
outputStateAndRef<S>().state
|
outputStateAndRef<S>().state
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the output [StateRef] based on the label.
|
||||||
|
* @see OutputStateLookup.retrieveOutputStateAndRef
|
||||||
|
*/
|
||||||
fun String.outputRef(): StateRef = outputStateAndRef<ContractState>().ref
|
fun String.outputRef(): StateRef = outputStateAndRef<ContractState>().ref
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,36 @@ import java.security.KeyPair
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Here is a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes.
|
||||||
|
//
|
||||||
|
// Define a transaction like this:
|
||||||
|
//
|
||||||
|
// ledger {
|
||||||
|
// transaction {
|
||||||
|
// input { someExpression }
|
||||||
|
// output { someExpression }
|
||||||
|
// command { someExpression }
|
||||||
|
//
|
||||||
|
// tweak {
|
||||||
|
// ... same thing but works with a copy of the parent, can add inputs/outputs/commands just within this scope.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// contract.verifies() -> verify() should pass
|
||||||
|
// contract `fails with` "some substring of the error message"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Here follows implementations of the [LedgerDSLInterpreter] and [TransactionDSLInterpreter] interfaces to be used in
|
||||||
|
* tests. Top level primitives [ledger] and [transaction] that bind the interpreter types are also defined here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see JavaTestHelpers.transaction
|
||||||
|
*/
|
||||||
fun transaction(
|
fun transaction(
|
||||||
transactionLabel: String? = null,
|
transactionLabel: String? = null,
|
||||||
transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
||||||
@ -23,6 +53,9 @@ fun transaction(
|
|||||||
>.() -> EnforceVerifyOrFail
|
>.() -> EnforceVerifyOrFail
|
||||||
) = JavaTestHelpers.transaction(transactionLabel, transactionBuilder, dsl)
|
) = JavaTestHelpers.transaction(transactionLabel, transactionBuilder, dsl)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see JavaTestHelpers.ledger
|
||||||
|
*/
|
||||||
fun ledger(
|
fun ledger(
|
||||||
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
||||||
storageService: StorageService = MockStorageService(),
|
storageService: StorageService = MockStorageService(),
|
||||||
@ -70,6 +103,7 @@ sealed class EnforceVerifyOrFail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DuplicateOutputLabel(label: String) : Exception("Output label '$label' already used")
|
class DuplicateOutputLabel(label: String) : Exception("Output label '$label' already used")
|
||||||
|
class AttachmentResolutionException(attachmentId: SecureHash) : Exception("Attachment with id $attachmentId not found")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interpreter builds a transaction, and [TransactionDSL.verifies] that the resolved transaction is correct. Note
|
* This interpreter builds a transaction, and [TransactionDSL.verifies] that the resolved transaction is correct. Note
|
||||||
@ -83,7 +117,7 @@ data class TestTransactionDSLInterpreter private constructor(
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
ledgerInterpreter: TestLedgerDSLInterpreter,
|
ledgerInterpreter: TestLedgerDSLInterpreter,
|
||||||
transactionBuilder: TransactionBuilder // = TransactionBuilder()
|
transactionBuilder: TransactionBuilder
|
||||||
) : this(ledgerInterpreter, transactionBuilder, HashMap())
|
) : this(ledgerInterpreter, transactionBuilder, HashMap())
|
||||||
|
|
||||||
private fun copy(): TestTransactionDSLInterpreter =
|
private fun copy(): TestTransactionDSLInterpreter =
|
||||||
@ -161,9 +195,6 @@ data class TestTransactionDSLInterpreter private constructor(
|
|||||||
) = dsl(TransactionDSL(copy()))
|
) = dsl(TransactionDSL(copy()))
|
||||||
}
|
}
|
||||||
|
|
||||||
class AttachmentResolutionException(attachmentId: SecureHash) :
|
|
||||||
Exception("Attachment with id $attachmentId not found")
|
|
||||||
|
|
||||||
data class TestLedgerDSLInterpreter private constructor (
|
data class TestLedgerDSLInterpreter private constructor (
|
||||||
private val identityService: IdentityService,
|
private val identityService: IdentityService,
|
||||||
private val storageService: StorageService,
|
private val storageService: StorageService,
|
||||||
@ -241,9 +272,9 @@ data class TestLedgerDSLInterpreter private constructor (
|
|||||||
internal fun resolveAttachment(attachmentId: SecureHash): Attachment =
|
internal fun resolveAttachment(attachmentId: SecureHash): Attachment =
|
||||||
storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
|
storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
|
||||||
|
|
||||||
private fun <Return> interpretTransactionDsl(
|
private fun <R> interpretTransactionDsl(
|
||||||
transactionBuilder: TransactionBuilder,
|
transactionBuilder: TransactionBuilder,
|
||||||
dsl: TransactionDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter>.() -> Return
|
dsl: TransactionDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter>.() -> R
|
||||||
): TestTransactionDSLInterpreter {
|
): TestTransactionDSLInterpreter {
|
||||||
val transactionInterpreter = TestTransactionDSLInterpreter(this, transactionBuilder)
|
val transactionInterpreter = TestTransactionDSLInterpreter(this, transactionBuilder)
|
||||||
dsl(TransactionDSL(transactionInterpreter))
|
dsl(TransactionDSL(transactionInterpreter))
|
||||||
@ -339,6 +370,12 @@ data class TestLedgerDSLInterpreter private constructor (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs all transactions passed in.
|
||||||
|
* @param transactionsToSign: Transactions to be signed.
|
||||||
|
* @param extraKeys: extra keys to sign transactions with.
|
||||||
|
* @return: List of [SignedTransaction]s.
|
||||||
|
*/
|
||||||
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyPair>) = transactionsToSign.map { wtx ->
|
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyPair>) = transactionsToSign.map { wtx ->
|
||||||
val allPubKeys = wtx.signers.toMutableSet()
|
val allPubKeys = wtx.signers.toMutableSet()
|
||||||
val bits = wtx.serialize()
|
val bits = wtx.serialize()
|
||||||
@ -353,6 +390,10 @@ fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyP
|
|||||||
SignedTransaction(bits, signatures)
|
SignedTransaction(bits, signatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs all transactions in the ledger.
|
||||||
|
* @param extraKeys: extra keys to sign transactions with.
|
||||||
|
* @return: List of [SignedTransaction]s.
|
||||||
|
*/
|
||||||
fun LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.signAll(
|
fun LedgerDSL<EnforceVerifyOrFail, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.signAll(
|
||||||
transactionsToSign: List<WireTransaction> = this.interpreter.wireTransactions, vararg extraKeys: KeyPair) =
|
vararg extraKeys: KeyPair) = signAll(this.interpreter.wireTransactions, extraKeys)
|
||||||
signAll(transactionsToSign, extraKeys)
|
|
||||||
|
@ -88,6 +88,12 @@ object JavaTestHelpers {
|
|||||||
return HostAndPort.fromParts("localhost", freePort)
|
return HostAndPort.fromParts("localhost", freePort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and tests a ledger built by the passed in dsl.
|
||||||
|
* @param identityService: The [IdentityService] to be used while building the ledger.
|
||||||
|
* @param storageService: The [StorageService] to be used for storing e.g. [Attachment]s.
|
||||||
|
* @param dsl: The dsl building the ledger.
|
||||||
|
*/
|
||||||
@JvmStatic @JvmOverloads fun ledger(
|
@JvmStatic @JvmOverloads fun ledger(
|
||||||
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
||||||
storageService: StorageService = MockStorageService(),
|
storageService: StorageService = MockStorageService(),
|
||||||
@ -98,6 +104,10 @@ object JavaTestHelpers {
|
|||||||
return ledgerDsl
|
return ledgerDsl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ledger with a single transaction, built by the passed in dsl.
|
||||||
|
* @see LedgerDSLInterpreter._transaction
|
||||||
|
*/
|
||||||
@JvmStatic @JvmOverloads fun transaction(
|
@JvmStatic @JvmOverloads fun transaction(
|
||||||
transactionLabel: String? = null,
|
transactionLabel: String? = null,
|
||||||
transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
transactionBuilder: TransactionBuilder = TransactionBuilder(),
|
||||||
|
@ -5,58 +5,79 @@ import com.r3corda.core.crypto.Party
|
|||||||
import com.r3corda.core.crypto.SecureHash
|
import com.r3corda.core.crypto.SecureHash
|
||||||
import com.r3corda.core.seconds
|
import com.r3corda.core.seconds
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Defines a simple DSL for building pseudo-transactions (not the same as the wire protocol) for testing purposes.
|
|
||||||
//
|
|
||||||
// Define a transaction like this:
|
|
||||||
//
|
|
||||||
// ledger {
|
|
||||||
// transaction {
|
|
||||||
// input { someExpression }
|
|
||||||
// output { someExpression }
|
|
||||||
// command { someExpression }
|
|
||||||
//
|
|
||||||
// tweak {
|
|
||||||
// ... same thing but works with a copy of the parent, can add inputs/outputs/commands just within this scope.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// contract.verifies() -> verify() should pass
|
|
||||||
// contract `fails with` "some substring of the error message"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [TransactionDSLInterpreter] defines the interface DSL interpreters should satisfy. No
|
* This interface defines the bare bone functionality that a Transaction DSL interpreter should implement.
|
||||||
* overloading/default valuing should be done here, only the basic functions that are required to implement everything.
|
* @param <R>: The return type of [verifies]/[failsWith] and the like. It is generic so that we have control over whether
|
||||||
* Same goes for functions requiring reflection e.g. [OutputStateLookup.retrieveOutputStateAndRef]
|
* we want to enforce users to call these methods (@see [EnforceVerifyOrFail]) or not.
|
||||||
* Put convenience functions in [TransactionDSL] instead. There are some cases where the overloads would clash with the
|
|
||||||
* Interpreter interface, in these cases define a "backing" function in the interface instead (e.g. [_command]).
|
|
||||||
*
|
|
||||||
* This way the responsibility of providing a nice frontend DSL and the implementation(s) are separated.
|
|
||||||
*/
|
*/
|
||||||
interface TransactionDSLInterpreter<R> : OutputStateLookup {
|
interface TransactionDSLInterpreter<R> : OutputStateLookup {
|
||||||
|
/**
|
||||||
|
* A reference to the enclosing ledger{..}'s interpreter.
|
||||||
|
*/
|
||||||
val ledgerInterpreter: LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>>
|
val ledgerInterpreter: LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an input reference to the transaction. Note that [verifies] will resolve this reference.
|
||||||
|
* @param stateRef: The input [StateRef].
|
||||||
|
*/
|
||||||
fun input(stateRef: StateRef)
|
fun input(stateRef: StateRef)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an output to the transaction.
|
||||||
|
* @param label: An optional label that may be later used to retrieve the output probably in other transactions.
|
||||||
|
* @param notary: The associated notary.
|
||||||
|
* @param contractState: The state itself.
|
||||||
|
*/
|
||||||
fun _output(label: String?, notary: Party, contractState: ContractState)
|
fun _output(label: String?, notary: Party, contractState: ContractState)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an [Attachment] reference to the transaction.
|
||||||
|
* @param attachmentId: The hash of the attachment, possibly returned by [LedgerDSLInterpreter.attachment]
|
||||||
|
*/
|
||||||
fun attachment(attachmentId: SecureHash)
|
fun attachment(attachmentId: SecureHash)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a command to the transaction.
|
||||||
|
* @param signers: The signer public keys.
|
||||||
|
* @param commandData: The contents of the command.
|
||||||
|
*/
|
||||||
fun _command(signers: List<PublicKey>, commandData: CommandData)
|
fun _command(signers: List<PublicKey>, commandData: CommandData)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the transaction.
|
||||||
|
* @return: Possibly a token confirming that [verifies] has been called.
|
||||||
|
*/
|
||||||
fun verifies(): R
|
fun verifies(): R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the transaction, expecting an exception to be thrown.
|
||||||
|
* @param expectedMessage: An optional string to be searched for in the raised exception.
|
||||||
|
*/
|
||||||
fun failsWith(expectedMessage: String?): R
|
fun failsWith(expectedMessage: String?): R
|
||||||
fun tweak(
|
|
||||||
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R
|
/**
|
||||||
): R
|
* Creates a local scoped copy of the transaction.
|
||||||
|
* @param dsl: The transaction DSL to be interpreted using the copy.
|
||||||
|
*/
|
||||||
|
fun tweak(dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R): R
|
||||||
}
|
}
|
||||||
|
|
||||||
class TransactionDSL<R, out T : TransactionDSLInterpreter<R>> (val interpreter: T) :
|
class TransactionDSL<R, out T : TransactionDSLInterpreter<R>> (val interpreter: T) :
|
||||||
TransactionDSLInterpreter<R> by interpreter {
|
TransactionDSLInterpreter<R> by interpreter {
|
||||||
|
|
||||||
fun input(stateLabel: String) = input(retrieveOutputStateAndRef(ContractState::class.java, stateLabel).ref)
|
|
||||||
/**
|
/**
|
||||||
* Adds the passed in state as a non-verified transaction output to the ledger and adds that as an input.
|
* Looks up the output label and adds the found state as an input.
|
||||||
|
* @param stateLabel: The label of the output state specified when calling [LedgerDSLInterpreter._output] and friends.
|
||||||
|
*/
|
||||||
|
fun input(stateLabel: String) = input(retrieveOutputStateAndRef(ContractState::class.java, stateLabel).ref)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an [LedgerDSLInterpreter._unverifiedTransaction] with a single output state and adds it's reference as an
|
||||||
|
* input to the current transaction.
|
||||||
|
* @param state: The state to be added.
|
||||||
*/
|
*/
|
||||||
fun input(state: ContractState) {
|
fun input(state: ContractState) {
|
||||||
val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder()) {
|
val transaction = ledgerInterpreter._unverifiedTransaction(null, TransactionBuilder()) {
|
||||||
@ -66,23 +87,53 @@ class TransactionDSL<R, out T : TransactionDSLInterpreter<R>> (val interpreter:
|
|||||||
}
|
}
|
||||||
fun input(stateClosure: () -> ContractState) = input(stateClosure())
|
fun input(stateClosure: () -> ContractState) = input(stateClosure())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see TransactionDSLInterpreter._output
|
||||||
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun output(label: String? = null, notary: Party = DUMMY_NOTARY, contractStateClosure: () -> ContractState) =
|
fun output(label: String? = null, notary: Party = DUMMY_NOTARY, contractStateClosure: () -> ContractState) =
|
||||||
_output(label, notary, contractStateClosure())
|
_output(label, notary, contractStateClosure())
|
||||||
|
/**
|
||||||
|
* @see TransactionDSLInterpreter._output
|
||||||
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun output(label: String? = null, contractState: ContractState) =
|
fun output(label: String? = null, contractState: ContractState) =
|
||||||
_output(label, DUMMY_NOTARY, contractState)
|
_output(label, DUMMY_NOTARY, contractState)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see TransactionDSLInterpreter._command
|
||||||
|
*/
|
||||||
fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) =
|
fun command(vararg signers: PublicKey, commandDataClosure: () -> CommandData) =
|
||||||
_command(listOf(*signers), commandDataClosure())
|
_command(listOf(*signers), commandDataClosure())
|
||||||
|
/**
|
||||||
|
* @see TransactionDSLInterpreter._command
|
||||||
|
*/
|
||||||
fun command(signer: PublicKey, commandData: CommandData) = _command(listOf(signer), commandData)
|
fun command(signer: PublicKey, commandData: CommandData) = _command(listOf(signer), commandData)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a timestamp command to the transaction.
|
||||||
|
* @param time: The [Instant] of the [TimestampCommand].
|
||||||
|
* @param tolerance: The tolerance of the [TimestampCommand].
|
||||||
|
* @param notary: The notary to sign the command.
|
||||||
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun timestamp(time: Instant, notary: PublicKey = DUMMY_NOTARY.owningKey) =
|
fun timestamp(time: Instant, tolerance: Duration = 30.seconds, notary: PublicKey = DUMMY_NOTARY.owningKey) =
|
||||||
timestamp(TimestampCommand(time, 30.seconds), notary)
|
timestamp(TimestampCommand(time, tolerance), notary)
|
||||||
|
/**
|
||||||
|
* Adds a timestamp command to the transaction.
|
||||||
|
* @param data: The [TimestampCommand].
|
||||||
|
* @param notary: The notary to sign the command.
|
||||||
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun timestamp(data: TimestampCommand, notary: PublicKey = DUMMY_NOTARY.owningKey) = command(notary, data)
|
fun timestamp(data: TimestampCommand, notary: PublicKey = DUMMY_NOTARY.owningKey) = command(notary, data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the transaction will fail verification
|
||||||
|
*/
|
||||||
fun fails() = failsWith(null)
|
fun fails() = failsWith(null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see TransactionDSLInterpreter.failsWith
|
||||||
|
*/
|
||||||
infix fun `fails with`(msg: String) = failsWith(msg)
|
infix fun `fails with`(msg: String) = failsWith(msg)
|
||||||
}
|
}
|
||||||
|
@ -484,7 +484,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
}
|
}
|
||||||
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||||
if (!withError)
|
if (!withError)
|
||||||
command(notary.owningKey) { TimestampCommand(TEST_TX_TIME, 30.seconds) }
|
timestamp(time = TEST_TX_TIME, notary = notary.owningKey)
|
||||||
if (attachmentID != null)
|
if (attachmentID != null)
|
||||||
attachment(attachmentID)
|
attachment(attachmentID)
|
||||||
if (withError) {
|
if (withError) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user