mirror of
https://github.com/corda/corda.git
synced 2025-02-21 09:51:57 +00:00
test dsl: Dsl->DSL, add top-level transaction primitive, add ledger-embedded signAll, other cosmetics
This commit is contained in:
parent
e3d6f51049
commit
e31b769fef
@ -66,7 +66,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
fun `trade lifecycle test`() {
|
fun `trade lifecycle test`() {
|
||||||
val someProfits = 1200.DOLLARS `issued by` issuer
|
val someProfits = 1200.DOLLARS `issued by` issuer
|
||||||
ledger {
|
ledger {
|
||||||
nonVerifiedTransaction {
|
unverifiedTransaction {
|
||||||
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
|
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
|
||||||
output("some profits", someProfits.STATE `owned by` MEGA_CORP_PUBKEY)
|
output("some profits", someProfits.STATE `owned by` MEGA_CORP_PUBKEY)
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ class CommercialPaperTestsGeneric {
|
|||||||
input("alice's paper")
|
input("alice's paper")
|
||||||
input("some profits")
|
input("some profits")
|
||||||
|
|
||||||
fun TransactionDsl<LastLineShouldTestForVerifiesOrFails, TransactionDslInterpreter<LastLineShouldTestForVerifiesOrFails>>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
|
fun TransactionDSL<LastLineShouldTestForVerifiesOrFails, TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
|
||||||
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY }
|
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY }
|
||||||
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY }
|
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY }
|
||||||
}
|
}
|
||||||
@ -131,53 +131,42 @@ class CommercialPaperTestsGeneric {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `key mismatch at issue`() {
|
fun `key mismatch at issue`() {
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
output { thisTest.getPaper() }
|
||||||
output { thisTest.getPaper() }
|
command(DUMMY_PUBKEY_1) { thisTest.getIssueCommand() }
|
||||||
command(DUMMY_PUBKEY_1) { thisTest.getIssueCommand() }
|
timestamp(TEST_TX_TIME)
|
||||||
timestamp(TEST_TX_TIME)
|
this `fails with` "signed by the claimed issuer"
|
||||||
this `fails with` "signed by the claimed issuer"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `face value is not zero`() {
|
fun `face value is not zero`() {
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
|
||||||
output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
timestamp(TEST_TX_TIME)
|
||||||
timestamp(TEST_TX_TIME)
|
this `fails with` "face value is not zero"
|
||||||
this `fails with` "face value is not zero"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `maturity date not in the past`() {
|
fun `maturity date not in the past`() {
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
|
||||||
output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
timestamp(TEST_TX_TIME)
|
||||||
timestamp(TEST_TX_TIME)
|
this `fails with` "maturity date is not in the past"
|
||||||
this `fails with` "maturity date is not in the past"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `issue cannot replace an existing state`() {
|
fun `issue cannot replace an existing state`() {
|
||||||
ledger {
|
transaction {
|
||||||
nonVerifiedTransaction {
|
input(thisTest.getPaper())
|
||||||
output("paper") { thisTest.getPaper() }
|
output { thisTest.getPaper() }
|
||||||
}
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
||||||
transaction {
|
timestamp(TEST_TX_TIME)
|
||||||
input("paper")
|
this `fails with` "there is no input state"
|
||||||
output { thisTest.getPaper() }
|
|
||||||
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "there is no input state"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +360,7 @@ class IRSTests {
|
|||||||
/**
|
/**
|
||||||
* Generates a typical transactional history for an IRS.
|
* Generates a typical transactional history for an IRS.
|
||||||
*/
|
*/
|
||||||
fun trade(): LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter> {
|
fun trade(): LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
|
||||||
|
|
||||||
val ld = LocalDate.of(2016, 3, 8)
|
val ld = LocalDate.of(2016, 3, 8)
|
||||||
val bd = BigDecimal("0.0063518")
|
val bd = BigDecimal("0.0063518")
|
||||||
@ -398,86 +398,76 @@ class IRSTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure failure occurs when there are inbound states for an agreement command`() {
|
fun `ensure failure occurs when there are inbound states for an agreement command`() {
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
input() { singleIRS() }
|
||||||
input() { singleIRS() }
|
output("irs post agreement") { singleIRS() }
|
||||||
output("irs post agreement") { singleIRS() }
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
timestamp(TEST_TX_TIME)
|
||||||
timestamp(TEST_TX_TIME)
|
this `fails with` "There are no in states for an agreement"
|
||||||
this `fails with` "There are no in states for an agreement"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure failure occurs when no events in fix schedule`() {
|
fun `ensure failure occurs when no events in fix schedule`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
val emptySchedule = HashMap<LocalDate, FixedRatePaymentEvent>()
|
||||||
val emptySchedule = HashMap<LocalDate, FixedRatePaymentEvent>()
|
transaction {
|
||||||
transaction {
|
output() {
|
||||||
output() {
|
irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))
|
||||||
irs.copy(calculation = irs.calculation.copy(fixedLegPaymentSchedule = emptySchedule))
|
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "There are events in the fix schedule"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "There are events in the fix schedule"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure failure occurs when no events in floating schedule`() {
|
fun `ensure failure occurs when no events in floating schedule`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
val emptySchedule = HashMap<LocalDate, FloatingRatePaymentEvent>()
|
||||||
val emptySchedule = HashMap<LocalDate, FloatingRatePaymentEvent>()
|
transaction {
|
||||||
transaction {
|
output() {
|
||||||
output() {
|
irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))
|
||||||
irs.copy(calculation = irs.calculation.copy(floatingLegPaymentSchedule = emptySchedule))
|
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "There are events in the float schedule"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "There are events in the float schedule"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure notionals are non zero`() {
|
fun `ensure notionals are non zero`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
transaction {
|
||||||
transaction {
|
output() {
|
||||||
output() {
|
irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))
|
||||||
irs.copy(irs.fixedLeg.copy(notional = irs.fixedLeg.notional.copy(quantity = 0)))
|
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "All notionals must be non zero"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "All notionals must be non zero"
|
||||||
|
}
|
||||||
|
|
||||||
transaction {
|
transaction {
|
||||||
output() {
|
output() {
|
||||||
irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))
|
irs.copy(irs.fixedLeg.copy(notional = irs.floatingLeg.notional.copy(quantity = 0)))
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "All notionals must be non zero"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "All notionals must be non zero"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure positive rate on fixed leg`() {
|
fun `ensure positive rate on fixed leg`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1"))))
|
||||||
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(fixedRate = FixedRate(PercentageRatioUnit("-0.1"))))
|
transaction {
|
||||||
transaction {
|
output() {
|
||||||
output() {
|
modifiedIRS
|
||||||
modifiedIRS
|
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "The fixed leg rate must be positive"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "The fixed leg rate must be positive"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,87 +476,79 @@ class IRSTests {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun `ensure same currency notionals`() {
|
fun `ensure same currency notionals`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY"))))
|
||||||
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.fixedLeg.notional.quantity, Currency.getInstance("JPY"))))
|
transaction {
|
||||||
transaction {
|
output() {
|
||||||
output() {
|
modifiedIRS
|
||||||
modifiedIRS
|
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "The currency of the notionals must be the same"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "The currency of the notionals must be the same"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure notional amounts are equal`() {
|
fun `ensure notional amounts are equal`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token)))
|
||||||
val modifiedIRS = irs.copy(fixedLeg = irs.fixedLeg.copy(notional = Amount(irs.floatingLeg.notional.quantity + 1, irs.floatingLeg.notional.token)))
|
transaction {
|
||||||
transaction {
|
output() {
|
||||||
output() {
|
modifiedIRS
|
||||||
modifiedIRS
|
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "All leg notionals must be the same"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "All leg notionals must be the same"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure trade date and termination date checks are done pt1`() {
|
fun `ensure trade date and termination date checks are done pt1`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1)))
|
||||||
val modifiedIRS1 = irs.copy(fixedLeg = irs.fixedLeg.copy(terminationDate = irs.fixedLeg.effectiveDate.minusDays(1)))
|
transaction {
|
||||||
transaction {
|
output() {
|
||||||
output() {
|
modifiedIRS1
|
||||||
modifiedIRS1
|
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "The effective date is before the termination date for the fixed leg"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "The effective date is before the termination date for the fixed leg"
|
||||||
|
}
|
||||||
|
|
||||||
val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1)))
|
val modifiedIRS2 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.floatingLeg.effectiveDate.minusDays(1)))
|
||||||
transaction {
|
transaction {
|
||||||
output() {
|
output() {
|
||||||
modifiedIRS2
|
modifiedIRS2
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "The effective date is before the termination date for the floating leg"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "The effective date is before the termination date for the floating leg"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ensure trade date and termination date checks are done pt2`() {
|
fun `ensure trade date and termination date checks are done pt2`() {
|
||||||
ledger {
|
val irs = singleIRS()
|
||||||
val irs = singleIRS()
|
|
||||||
|
|
||||||
val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1)))
|
val modifiedIRS3 = irs.copy(floatingLeg = irs.floatingLeg.copy(terminationDate = irs.fixedLeg.terminationDate.minusDays(1)))
|
||||||
transaction {
|
transaction {
|
||||||
output() {
|
output() {
|
||||||
modifiedIRS3
|
modifiedIRS3
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "The termination dates are aligned"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "The termination dates are aligned"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1)))
|
val modifiedIRS4 = irs.copy(floatingLeg = irs.floatingLeg.copy(effectiveDate = irs.fixedLeg.effectiveDate.minusDays(1)))
|
||||||
transaction {
|
transaction {
|
||||||
output() {
|
output() {
|
||||||
modifiedIRS4
|
modifiedIRS4
|
||||||
}
|
|
||||||
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "The effective dates are aligned"
|
|
||||||
}
|
}
|
||||||
|
command(MEGA_CORP_PUBKEY) { InterestRateSwap.Commands.Agree() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails with` "The effective dates are aligned"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,7 +656,7 @@ class IRSTests {
|
|||||||
* result and the grouping won't work either.
|
* result and the grouping won't work either.
|
||||||
* In reality, the only fields that should be in common will be the next fixing date and the reference rate.
|
* In reality, the only fields that should be in common will be the next fixing date and the reference rate.
|
||||||
*/
|
*/
|
||||||
fun tradegroups(): LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter> {
|
fun tradegroups(): LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
|
||||||
val ld1 = LocalDate.of(2016, 3, 8)
|
val ld1 = LocalDate.of(2016, 3, 8)
|
||||||
val bd1 = BigDecimal("0.0063518")
|
val bd1 = BigDecimal("0.0063518")
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.r3corda.contracts.testing.*
|
|||||||
import com.r3corda.core.contracts.*
|
import com.r3corda.core.contracts.*
|
||||||
import com.r3corda.core.crypto.SecureHash
|
import com.r3corda.core.crypto.SecureHash
|
||||||
import com.r3corda.core.testing.*
|
import com.r3corda.core.testing.*
|
||||||
|
import com.r3corda.core.testing.JavaTestHelpers
|
||||||
import com.r3corda.core.utilities.nonEmptySetOf
|
import com.r3corda.core.utilities.nonEmptySetOf
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
@ -34,9 +35,9 @@ class ObligationTests {
|
|||||||
val outState = inState.copy(beneficiary = DUMMY_PUBKEY_2)
|
val outState = inState.copy(beneficiary = DUMMY_PUBKEY_2)
|
||||||
|
|
||||||
private fun obligationTestRoots(
|
private fun obligationTestRoots(
|
||||||
group: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>
|
group: LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>
|
||||||
) = group.apply {
|
) = group.apply {
|
||||||
nonVerifiedTransaction {
|
unverifiedTransaction {
|
||||||
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY))
|
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY))
|
||||||
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION `between` Pair(BOB, ALICE_PUBKEY))
|
output("Bob's $1,000,000 obligation to Alice", oneMillionDollars.OBLIGATION `between` Pair(BOB, ALICE_PUBKEY))
|
||||||
output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(MEGA_CORP, BOB_PUBKEY))
|
output("MegaCorp's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(MEGA_CORP, BOB_PUBKEY))
|
||||||
@ -46,37 +47,35 @@ class ObligationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun trivial() {
|
fun trivial() {
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
input { inState }
|
||||||
input { inState }
|
this `fails with` "the amounts balance"
|
||||||
this `fails with` "the amounts balance"
|
|
||||||
|
|
||||||
tweak {
|
tweak {
|
||||||
output { outState.copy(quantity = 2000.DOLLARS.quantity) }
|
output { outState.copy(quantity = 2000.DOLLARS.quantity) }
|
||||||
this `fails with` "the amounts balance"
|
this `fails with` "the amounts balance"
|
||||||
}
|
}
|
||||||
tweak {
|
tweak {
|
||||||
output { outState }
|
output { outState }
|
||||||
// No command commanduments
|
// No command commanduments
|
||||||
this `fails with` "required com.r3corda.contracts.asset.Obligation.Commands.Move command"
|
this `fails with` "required com.r3corda.contracts.asset.Obligation.Commands.Move command"
|
||||||
}
|
}
|
||||||
tweak {
|
tweak {
|
||||||
output { outState }
|
output { outState }
|
||||||
command(DUMMY_PUBKEY_2) { Obligation.Commands.Move(inState.issuanceDef) }
|
command(DUMMY_PUBKEY_2) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
this `fails with` "the owning keys are the same as the signing keys"
|
this `fails with` "the owning keys are the same as the signing keys"
|
||||||
}
|
}
|
||||||
tweak {
|
tweak {
|
||||||
output { outState }
|
output { outState }
|
||||||
output { outState `issued by` MINI_CORP }
|
output { outState `issued by` MINI_CORP }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
this `fails with` "at least one obligation input"
|
this `fails with` "at least one obligation input"
|
||||||
}
|
}
|
||||||
// Simple reallocation works.
|
// Simple reallocation works.
|
||||||
tweak {
|
tweak {
|
||||||
output { outState }
|
output { outState }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,111 +83,109 @@ class ObligationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun `issue debt`() {
|
fun `issue debt`() {
|
||||||
// Check we can't "move" debt into existence.
|
// Check we can't "move" debt into existence.
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
input { DummyState() }
|
||||||
input { DummyState() }
|
output { outState }
|
||||||
output { outState }
|
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move(outState.issuanceDef) }
|
||||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Move(outState.issuanceDef) }
|
|
||||||
|
|
||||||
this `fails with` "there is at least one obligation input"
|
this `fails with` "there is at least one obligation input"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised
|
||||||
|
// institution is allowed to issue as much cash as they want.
|
||||||
|
transaction {
|
||||||
|
output { outState }
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Issue(outState.issuanceDef) }
|
||||||
|
this `fails with` "output deposits are owned by a command signer"
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
output {
|
||||||
|
Obligation.State(
|
||||||
|
obligor = MINI_CORP,
|
||||||
|
quantity = 1000.DOLLARS.quantity,
|
||||||
|
beneficiary = DUMMY_PUBKEY_1,
|
||||||
|
template = megaCorpDollarSettlement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
tweak {
|
||||||
|
command(MINI_CORP_PUBKEY) { Obligation.Commands.Issue(Obligation.IssuanceDefinition(MINI_CORP, megaCorpDollarSettlement), 0) }
|
||||||
|
this `fails with` "has a nonce"
|
||||||
|
}
|
||||||
|
command(MINI_CORP_PUBKEY) { Obligation.Commands.Issue(Obligation.IssuanceDefinition(MINI_CORP, megaCorpDollarSettlement)) }
|
||||||
|
this.verifies()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test generation works.
|
||||||
|
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
||||||
|
Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
||||||
|
beneficiary = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY)
|
||||||
|
assertTrue(ptx.inputStates().isEmpty())
|
||||||
|
val expected = Obligation.State(
|
||||||
|
obligor = MINI_CORP,
|
||||||
|
quantity = 100.DOLLARS.quantity,
|
||||||
|
beneficiary = DUMMY_PUBKEY_1,
|
||||||
|
template = megaCorpDollarSettlement
|
||||||
|
)
|
||||||
|
assertEquals(ptx.outputStates()[0].data, expected)
|
||||||
|
assertTrue(ptx.commands()[0].value is Obligation.Commands.Issue<*>)
|
||||||
|
assertEquals(MINI_CORP_PUBKEY, ptx.commands()[0].signers[0])
|
||||||
|
|
||||||
|
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
|
||||||
|
transaction {
|
||||||
|
input { inState }
|
||||||
|
output { inState.copy(quantity = inState.amount.quantity * 2) }
|
||||||
|
|
||||||
|
// Move fails: not allowed to summon money.
|
||||||
|
tweak {
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
|
this `fails with` "at obligor MegaCorp the amounts balance"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check we can issue money only as long as the issuer institution is a command signer, i.e. any recognised
|
// Issue works.
|
||||||
// institution is allowed to issue as much cash as they want.
|
tweak {
|
||||||
transaction {
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
||||||
output { outState }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Issue(outState.issuanceDef) }
|
|
||||||
this `fails with` "output deposits are owned by a command signer"
|
|
||||||
}
|
|
||||||
transaction {
|
|
||||||
output {
|
|
||||||
Obligation.State(
|
|
||||||
obligor = MINI_CORP,
|
|
||||||
quantity = 1000.DOLLARS.quantity,
|
|
||||||
beneficiary = DUMMY_PUBKEY_1,
|
|
||||||
template = megaCorpDollarSettlement
|
|
||||||
)
|
|
||||||
}
|
|
||||||
tweak {
|
|
||||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Issue(Obligation.IssuanceDefinition(MINI_CORP, megaCorpDollarSettlement), 0) }
|
|
||||||
this `fails with` "has a nonce"
|
|
||||||
}
|
|
||||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Issue(Obligation.IssuanceDefinition(MINI_CORP, megaCorpDollarSettlement)) }
|
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test generation works.
|
// Can't use an issue command to lower the amount.
|
||||||
val ptx = TransactionType.General.Builder(DUMMY_NOTARY)
|
transaction {
|
||||||
Obligation<Currency>().generateIssue(ptx, MINI_CORP, megaCorpDollarSettlement, 100.DOLLARS.quantity,
|
input { inState }
|
||||||
beneficiary = DUMMY_PUBKEY_1, notary = DUMMY_NOTARY)
|
output { inState.copy(quantity = inState.amount.quantity / 2) }
|
||||||
assertTrue(ptx.inputStates().isEmpty())
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
||||||
val expected = Obligation.State(
|
this `fails with` "output values sum to more than the inputs"
|
||||||
obligor = MINI_CORP,
|
}
|
||||||
quantity = 100.DOLLARS.quantity,
|
|
||||||
beneficiary = DUMMY_PUBKEY_1,
|
|
||||||
template = megaCorpDollarSettlement
|
|
||||||
)
|
|
||||||
assertEquals(ptx.outputStates()[0].data, expected)
|
|
||||||
assertTrue(ptx.commands()[0].value is Obligation.Commands.Issue<*>)
|
|
||||||
assertEquals(MINI_CORP_PUBKEY, ptx.commands()[0].signers[0])
|
|
||||||
|
|
||||||
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
|
// Can't have an issue command that doesn't actually issue money.
|
||||||
transaction {
|
transaction {
|
||||||
input { inState }
|
input { inState }
|
||||||
output { inState.copy(quantity = inState.amount.quantity * 2) }
|
output { inState }
|
||||||
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
||||||
|
this `fails with` "output values sum to more than the inputs"
|
||||||
|
}
|
||||||
|
|
||||||
// Move fails: not allowed to summon money.
|
// Can't have any other commands if we have an issue command (because the issue command overrules them)
|
||||||
tweak {
|
transaction {
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
input { inState }
|
||||||
this `fails with` "at obligor MegaCorp the amounts balance"
|
output { inState.copy(quantity = inState.amount.quantity * 2) }
|
||||||
}
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
||||||
|
tweak {
|
||||||
// Issue works.
|
|
||||||
tweak {
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't use an issue command to lower the amount.
|
|
||||||
transaction {
|
|
||||||
input { inState }
|
|
||||||
output { inState.copy(quantity = inState.amount.quantity / 2) }
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
||||||
this `fails with` "output values sum to more than the inputs"
|
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
||||||
}
|
}
|
||||||
|
tweak {
|
||||||
// Can't have an issue command that doesn't actually issue money.
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
transaction {
|
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
||||||
input { inState }
|
|
||||||
output { inState }
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
|
||||||
this `fails with` "output values sum to more than the inputs"
|
|
||||||
}
|
}
|
||||||
|
tweak {
|
||||||
// Can't have any other commands if we have an issue command (because the issue command overrules them)
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.SetLifecycle(inState.issuanceDef, Lifecycle.DEFAULTED) }
|
||||||
transaction {
|
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
||||||
input { inState }
|
|
||||||
output { inState.copy(quantity = inState.amount.quantity * 2) }
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
|
||||||
tweak {
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Issue(inState.issuanceDef) }
|
|
||||||
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
|
||||||
}
|
|
||||||
tweak {
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Move(inState.issuanceDef) }
|
|
||||||
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
|
||||||
}
|
|
||||||
tweak {
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.SetLifecycle(inState.issuanceDef, Lifecycle.DEFAULTED) }
|
|
||||||
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
|
||||||
}
|
|
||||||
tweak {
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, inState.amount / 2) }
|
|
||||||
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
|
||||||
}
|
|
||||||
this.verifies()
|
|
||||||
}
|
}
|
||||||
|
tweak {
|
||||||
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, inState.amount / 2) }
|
||||||
|
this `fails with` "only move/exit commands can be present along with other obligation commands"
|
||||||
|
}
|
||||||
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +385,6 @@ class ObligationTests {
|
|||||||
this `fails with` "any involved party has signed"
|
this `fails with` "any involved party has signed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -480,26 +476,18 @@ class ObligationTests {
|
|||||||
// Try defaulting an obligation due in the future
|
// Try defaulting an obligation due in the future
|
||||||
val pastTestTime = TEST_TX_TIME - Duration.ofDays(7)
|
val pastTestTime = TEST_TX_TIME - Duration.ofDays(7)
|
||||||
val futureTestTime = TEST_TX_TIME + Duration.ofDays(7)
|
val futureTestTime = TEST_TX_TIME + Duration.ofDays(7)
|
||||||
ledger {
|
transaction("Settlement") {
|
||||||
nonVerifiedTransaction {
|
input(oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` futureTestTime)
|
||||||
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` futureTestTime)
|
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
||||||
}
|
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF) `at` futureTestTime, Lifecycle.DEFAULTED) }
|
||||||
transaction("Settlement") {
|
timestamp(TEST_TX_TIME)
|
||||||
input("Alice's $1,000,000 obligation to Bob")
|
this `fails with` "the due date has passed"
|
||||||
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` futureTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
|
||||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF) `at` futureTestTime, Lifecycle.DEFAULTED) }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
this `fails with` "the due date has passed"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try defaulting an obligation that is now in the past
|
// Try defaulting an obligation that is now in the past
|
||||||
ledger {
|
ledger {
|
||||||
nonVerifiedTransaction {
|
|
||||||
output("Alice's $1,000,000 obligation to Bob", oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` pastTestTime)
|
|
||||||
}
|
|
||||||
transaction("Settlement") {
|
transaction("Settlement") {
|
||||||
input("Alice's $1,000,000 obligation to Bob")
|
input(oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` pastTestTime)
|
||||||
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
output("Alice's defaulted $1,000,000 obligation to Bob") { (oneMillionDollars.OBLIGATION `between` Pair(ALICE, BOB_PUBKEY) `at` pastTestTime).copy(lifecycle = Lifecycle.DEFAULTED) }
|
||||||
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF) `at` pastTestTime, Lifecycle.DEFAULTED) }
|
command(BOB_PUBKEY) { Obligation.Commands.SetLifecycle(Obligation.IssuanceDefinition(ALICE, defaultUsd.OBLIGATION_DEF) `at` pastTestTime, Lifecycle.DEFAULTED) }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
@ -512,182 +500,171 @@ class ObligationTests {
|
|||||||
@Test
|
@Test
|
||||||
fun testMergeSplit() {
|
fun testMergeSplit() {
|
||||||
// Splitting value works.
|
// Splitting value works.
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
tweak {
|
||||||
tweak {
|
input { inState }
|
||||||
input { inState }
|
repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } }
|
||||||
repeat(4) { output { inState.copy(quantity = inState.quantity / 4) } }
|
this.verifies()
|
||||||
this.verifies()
|
}
|
||||||
}
|
// Merging 4 inputs into 2 outputs works.
|
||||||
// Merging 4 inputs into 2 outputs works.
|
tweak {
|
||||||
tweak {
|
repeat(4) { input { inState.copy(quantity = inState.quantity / 4) } }
|
||||||
repeat(4) { input { inState.copy(quantity = inState.quantity / 4) } }
|
output { inState.copy(quantity = inState.quantity / 2) }
|
||||||
output { inState.copy(quantity = inState.quantity / 2) }
|
output { inState.copy(quantity = inState.quantity / 2) }
|
||||||
output { inState.copy(quantity = inState.quantity / 2) }
|
this.verifies()
|
||||||
this.verifies()
|
}
|
||||||
}
|
// Merging 2 inputs into 1 works.
|
||||||
// Merging 2 inputs into 1 works.
|
tweak {
|
||||||
tweak {
|
input { inState.copy(quantity = inState.quantity / 2) }
|
||||||
input { inState.copy(quantity = inState.quantity / 2) }
|
input { inState.copy(quantity = inState.quantity / 2) }
|
||||||
input { inState.copy(quantity = inState.quantity / 2) }
|
output { inState }
|
||||||
output { inState }
|
this.verifies()
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun zeroSizedValues() {
|
fun zeroSizedValues() {
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
input { inState }
|
||||||
input { inState }
|
input { inState.copy(quantity = 0L) }
|
||||||
input { inState.copy(quantity = 0L) }
|
this `fails with` "zero sized inputs"
|
||||||
this `fails with` "zero sized inputs"
|
}
|
||||||
}
|
transaction {
|
||||||
transaction {
|
input { inState }
|
||||||
input { inState }
|
output { inState }
|
||||||
output { inState }
|
output { inState.copy(quantity = 0L) }
|
||||||
output { inState.copy(quantity = 0L) }
|
this `fails with` "zero sized outputs"
|
||||||
this `fails with` "zero sized outputs"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun trivialMismatches() {
|
fun trivialMismatches() {
|
||||||
ledger {
|
// Can't change issuer.
|
||||||
// Can't change issuer.
|
transaction {
|
||||||
transaction {
|
input { inState }
|
||||||
input { inState }
|
output { outState `issued by` MINI_CORP }
|
||||||
output { outState `issued by` MINI_CORP }
|
this `fails with` "at obligor MegaCorp the amounts balance"
|
||||||
this `fails with` "at obligor MegaCorp the amounts balance"
|
}
|
||||||
}
|
// Can't mix currencies.
|
||||||
// Can't mix currencies.
|
transaction {
|
||||||
transaction {
|
input { inState }
|
||||||
input { inState }
|
output { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) }
|
||||||
output { outState.copy(quantity = 80000, template = megaCorpDollarSettlement) }
|
output { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) }
|
||||||
output { outState.copy(quantity = 20000, template = megaCorpPoundSettlement) }
|
this `fails with` "the amounts balance"
|
||||||
this `fails with` "the amounts balance"
|
}
|
||||||
}
|
transaction {
|
||||||
transaction {
|
input { inState }
|
||||||
input { inState }
|
input {
|
||||||
input {
|
inState.copy(
|
||||||
inState.copy(
|
quantity = 15000,
|
||||||
quantity = 15000,
|
template = megaCorpPoundSettlement,
|
||||||
template = megaCorpPoundSettlement,
|
beneficiary = DUMMY_PUBKEY_2
|
||||||
beneficiary = DUMMY_PUBKEY_2
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
output { outState.copy(quantity = 115000) }
|
|
||||||
this `fails with` "the amounts balance"
|
|
||||||
}
|
|
||||||
// Can't have superfluous input states from different issuers.
|
|
||||||
transaction {
|
|
||||||
input { inState }
|
|
||||||
input { inState `issued by` MINI_CORP }
|
|
||||||
output { outState }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
|
||||||
this `fails with` "at obligor MiniCorp the amounts balance"
|
|
||||||
}
|
}
|
||||||
|
output { outState.copy(quantity = 115000) }
|
||||||
|
this `fails with` "the amounts balance"
|
||||||
|
}
|
||||||
|
// Can't have superfluous input states from different issuers.
|
||||||
|
transaction {
|
||||||
|
input { inState }
|
||||||
|
input { inState `issued by` MINI_CORP }
|
||||||
|
output { outState }
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
||||||
|
this `fails with` "at obligor MiniCorp the amounts balance"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun exitLedger() {
|
fun exitLedger() {
|
||||||
ledger {
|
// Single input/output straightforward case.
|
||||||
// Single input/output straightforward case.
|
transaction {
|
||||||
transaction {
|
input { inState }
|
||||||
input { inState }
|
output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
|
||||||
output { outState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
|
|
||||||
|
|
||||||
tweak {
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, 100.DOLLARS) }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
|
||||||
this `fails with` "the amounts balance"
|
|
||||||
}
|
|
||||||
|
|
||||||
tweak {
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, 200.DOLLARS) }
|
|
||||||
this `fails with` "required com.r3corda.contracts.asset.Obligation.Commands.Move command"
|
|
||||||
|
|
||||||
tweak {
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Multi-issuer case.
|
|
||||||
transaction {
|
|
||||||
input { inState }
|
|
||||||
input { inState `issued by` MINI_CORP }
|
|
||||||
|
|
||||||
output { inState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) `issued by` MINI_CORP }
|
|
||||||
output { inState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
|
|
||||||
|
|
||||||
|
tweak {
|
||||||
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, 100.DOLLARS) }
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
|
this `fails with` "the amounts balance"
|
||||||
this `fails with` "at obligor MegaCorp the amounts balance"
|
|
||||||
|
|
||||||
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, 200.DOLLARS) }
|
|
||||||
tweak {
|
|
||||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Exit((inState `issued by` MINI_CORP).issuanceDef, 0.DOLLARS) }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
|
||||||
this `fails with` "at obligor MiniCorp the amounts balance"
|
|
||||||
}
|
|
||||||
command(MINI_CORP_PUBKEY) { Obligation.Commands.Exit((inState `issued by` MINI_CORP).issuanceDef, 200.DOLLARS) }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
|
||||||
this.verifies()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tweak {
|
||||||
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, 200.DOLLARS) }
|
||||||
|
this `fails with` "required com.r3corda.contracts.asset.Obligation.Commands.Move command"
|
||||||
|
|
||||||
|
tweak {
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
|
this.verifies()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Multi-issuer case.
|
||||||
|
transaction {
|
||||||
|
input { inState }
|
||||||
|
input { inState `issued by` MINI_CORP }
|
||||||
|
|
||||||
|
output { inState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) `issued by` MINI_CORP }
|
||||||
|
output { inState.copy(quantity = inState.quantity - 200.DOLLARS.quantity) }
|
||||||
|
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
|
|
||||||
|
this `fails with` "at obligor MegaCorp the amounts balance"
|
||||||
|
|
||||||
|
command(MEGA_CORP_PUBKEY) { Obligation.Commands.Exit(inState.issuanceDef, 200.DOLLARS) }
|
||||||
|
tweak {
|
||||||
|
command(MINI_CORP_PUBKEY) { Obligation.Commands.Exit((inState `issued by` MINI_CORP).issuanceDef, 0.DOLLARS) }
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
||||||
|
this `fails with` "at obligor MiniCorp the amounts balance"
|
||||||
|
}
|
||||||
|
command(MINI_CORP_PUBKEY) { Obligation.Commands.Exit((inState `issued by` MINI_CORP).issuanceDef, 200.DOLLARS) }
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
||||||
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multiIssuer() {
|
fun multiIssuer() {
|
||||||
ledger {
|
transaction {
|
||||||
transaction {
|
// Gather 2000 dollars from two different issuers.
|
||||||
// Gather 2000 dollars from two different issuers.
|
input { inState }
|
||||||
input { inState }
|
input { inState `issued by` MINI_CORP }
|
||||||
input { inState `issued by` MINI_CORP }
|
|
||||||
|
|
||||||
// Can't merge them together.
|
// Can't merge them together.
|
||||||
tweak {
|
tweak {
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2, quantity = 200000L) }
|
output { inState.copy(beneficiary = DUMMY_PUBKEY_2, quantity = 200000L) }
|
||||||
this `fails with` "at obligor MegaCorp the amounts balance"
|
this `fails with` "at obligor MegaCorp the amounts balance"
|
||||||
}
|
|
||||||
// Missing MiniCorp deposit
|
|
||||||
tweak {
|
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
|
||||||
this `fails with` "at obligor MegaCorp the amounts balance"
|
|
||||||
}
|
|
||||||
|
|
||||||
// This works.
|
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
|
||||||
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) `issued by` MINI_CORP }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
|
||||||
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
|
||||||
this.verifies()
|
|
||||||
}
|
}
|
||||||
|
// Missing MiniCorp deposit
|
||||||
|
tweak {
|
||||||
|
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
||||||
|
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
||||||
|
this `fails with` "at obligor MegaCorp the amounts balance"
|
||||||
|
}
|
||||||
|
|
||||||
|
// This works.
|
||||||
|
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) }
|
||||||
|
output { inState.copy(beneficiary = DUMMY_PUBKEY_2) `issued by` MINI_CORP }
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
|
command(DUMMY_PUBKEY_1) { Obligation.Commands.Move((inState `issued by` MINI_CORP).issuanceDef) }
|
||||||
|
this.verifies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multiCurrency() {
|
fun multiCurrency() {
|
||||||
ledger {
|
// Check we can do an atomic currency trade tx.
|
||||||
// Check we can do an atomic currency trade tx.
|
transaction {
|
||||||
transaction {
|
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, DUMMY_PUBKEY_2)
|
||||||
val pounds = Obligation.State(Lifecycle.NORMAL, MINI_CORP, megaCorpPoundSettlement, 658.POUNDS.quantity, DUMMY_PUBKEY_2)
|
input { inState `owned by` DUMMY_PUBKEY_1 }
|
||||||
input { inState `owned by` DUMMY_PUBKEY_1 }
|
input { pounds }
|
||||||
input { pounds }
|
output { inState `owned by` DUMMY_PUBKEY_2 }
|
||||||
output { inState `owned by` DUMMY_PUBKEY_2 }
|
output { pounds `owned by` DUMMY_PUBKEY_1 }
|
||||||
output { pounds `owned by` DUMMY_PUBKEY_1 }
|
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Obligation.Commands.Move(inState.issuanceDef) }
|
||||||
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Obligation.Commands.Move(inState.issuanceDef) }
|
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Obligation.Commands.Move(pounds.issuanceDef) }
|
||||||
command(DUMMY_PUBKEY_1, DUMMY_PUBKEY_2) { Obligation.Commands.Move(pounds.issuanceDef) }
|
|
||||||
|
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.r3corda.core.testing
|
||||||
|
|
||||||
|
import com.r3corda.core.contracts.*
|
||||||
|
import com.r3corda.core.crypto.SecureHash
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
interface OutputStateLookup {
|
||||||
|
fun <S : ContractState> retrieveOutputStateAndRef(clazz: Class<S>, label: String): StateAndRef<S>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LedgerDSLInterpreter<R, out T : TransactionDSLInterpreter<R>> : OutputStateLookup {
|
||||||
|
fun transaction(transactionLabel: String?, dsl: TransactionDSL<R, T>.() -> R): WireTransaction
|
||||||
|
fun unverifiedTransaction(transactionLabel: String?, dsl: TransactionDSL<R, T>.() -> Unit): WireTransaction
|
||||||
|
fun tweak(dsl: LedgerDSL<R, T, LedgerDSLInterpreter<R, T>>.() -> Unit)
|
||||||
|
fun attachment(attachment: InputStream): SecureHash
|
||||||
|
fun verifies()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the class the top-level primitives deal with. It delegates all other primitives 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
|
||||||
|
* covariance of the TransactionInterpreter parameter
|
||||||
|
*/
|
||||||
|
class LedgerDSL<R, out T : TransactionDSLInterpreter<R>, out L : LedgerDSLInterpreter<R, T>> (val interpreter: L) :
|
||||||
|
LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>> by interpreter {
|
||||||
|
|
||||||
|
fun transaction(dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R) =
|
||||||
|
transaction(null, dsl)
|
||||||
|
fun unverifiedTransaction(dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> Unit) =
|
||||||
|
unverifiedTransaction(null, dsl)
|
||||||
|
|
||||||
|
inline fun <reified S : ContractState> String.outputStateAndRef(): StateAndRef<S> =
|
||||||
|
retrieveOutputStateAndRef(S::class.java, this)
|
||||||
|
inline fun <reified S : ContractState> String.output(): TransactionState<S> =
|
||||||
|
outputStateAndRef<S>().state
|
||||||
|
fun String.outputRef(): StateRef = outputStateAndRef<ContractState>().ref
|
||||||
|
}
|
@ -1,48 +0,0 @@
|
|||||||
package com.r3corda.core.testing
|
|
||||||
|
|
||||||
import com.r3corda.core.contracts.*
|
|
||||||
import com.r3corda.core.crypto.SecureHash
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
interface OutputStateLookup {
|
|
||||||
fun <State: ContractState> retrieveOutputStateAndRef(clazz: Class<State>, label: String): StateAndRef<State>
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LedgerDslInterpreter<Return, out TransactionInterpreter: TransactionDslInterpreter<Return>> :
|
|
||||||
OutputStateLookup {
|
|
||||||
fun transaction(
|
|
||||||
transactionLabel: String?,
|
|
||||||
dsl: TransactionDsl<Return, TransactionInterpreter>.() -> Return
|
|
||||||
): WireTransaction
|
|
||||||
fun nonVerifiedTransaction(
|
|
||||||
transactionLabel: String?,
|
|
||||||
dsl: TransactionDsl<Return, TransactionInterpreter>.() -> Unit
|
|
||||||
): WireTransaction
|
|
||||||
fun tweak(dsl: LedgerDsl<Return, TransactionInterpreter, LedgerDslInterpreter<Return, TransactionInterpreter>>.() -> Unit)
|
|
||||||
fun attachment(attachment: InputStream): SecureHash
|
|
||||||
fun verifies()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the class the top-level primitives deal with. It delegates all other primitives 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
|
|
||||||
* covariance of the TransactionInterpreter parameter
|
|
||||||
*/
|
|
||||||
class LedgerDsl<
|
|
||||||
Return,
|
|
||||||
out TransactionInterpreter: TransactionDslInterpreter<Return>,
|
|
||||||
out LedgerInterpreter: LedgerDslInterpreter<Return, TransactionInterpreter>
|
|
||||||
> (val interpreter: LedgerInterpreter
|
|
||||||
) : LedgerDslInterpreter<Return, TransactionDslInterpreter<Return>> by interpreter {
|
|
||||||
|
|
||||||
fun transaction(dsl: TransactionDsl<Return, TransactionDslInterpreter<Return>>.() -> Return) =
|
|
||||||
transaction(null, dsl)
|
|
||||||
fun nonVerifiedTransaction(dsl: TransactionDsl<Return, TransactionDslInterpreter<Return>>.() -> Unit) =
|
|
||||||
nonVerifiedTransaction(null, dsl)
|
|
||||||
|
|
||||||
inline fun <reified State: ContractState> String.outputStateAndRef(): StateAndRef<State> =
|
|
||||||
retrieveOutputStateAndRef(State::class.java, this)
|
|
||||||
inline fun <reified State: ContractState> String.output(): TransactionState<State> =
|
|
||||||
outputStateAndRef<State>().state
|
|
||||||
fun String.outputRef(): StateRef = outputStateAndRef<ContractState>().ref
|
|
||||||
}
|
|
@ -14,56 +14,75 @@ import java.security.KeyPair
|
|||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
fun transaction(
|
||||||
|
transactionLabel: String? = null,
|
||||||
|
dsl: TransactionDSL<
|
||||||
|
LastLineShouldTestForVerifiesOrFails,
|
||||||
|
TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>
|
||||||
|
>.() -> LastLineShouldTestForVerifiesOrFails
|
||||||
|
) = JavaTestHelpers.transaction(transactionLabel, dsl)
|
||||||
|
|
||||||
fun ledger(
|
fun ledger(
|
||||||
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
||||||
storageService: StorageService = MockStorageService(),
|
storageService: StorageService = MockStorageService(),
|
||||||
dsl: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.() -> Unit
|
dsl: LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
|
||||||
) = JavaTestHelpers.ledger(identityService, storageService, dsl)
|
) = JavaTestHelpers.ledger(identityService, storageService, dsl)
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
message = "ledger doesn't nest, use tweak",
|
message = "ledger doesn't nest, use tweak",
|
||||||
replaceWith = ReplaceWith("tweak"),
|
replaceWith = ReplaceWith("tweak"),
|
||||||
level = DeprecationLevel.ERROR)
|
level = DeprecationLevel.ERROR)
|
||||||
fun TransactionDslInterpreter<LastLineShouldTestForVerifiesOrFails>.ledger(
|
@Suppress("UNUSED_PARAMETER")
|
||||||
dsl: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.() -> Unit) {
|
fun TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>.ledger(
|
||||||
this.toString()
|
dsl: LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit) {
|
||||||
dsl.toString()
|
}
|
||||||
|
|
||||||
|
@Deprecated(
|
||||||
|
message = "transaction doesn't nest, use tweak",
|
||||||
|
replaceWith = ReplaceWith("tweak"),
|
||||||
|
level = DeprecationLevel.ERROR)
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
fun TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>.transaction(
|
||||||
|
dsl: TransactionDSL<
|
||||||
|
LastLineShouldTestForVerifiesOrFails,
|
||||||
|
TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>
|
||||||
|
>.() -> LastLineShouldTestForVerifiesOrFails) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
message = "ledger doesn't nest, use tweak",
|
message = "ledger doesn't nest, use tweak",
|
||||||
replaceWith = ReplaceWith("tweak"),
|
replaceWith = ReplaceWith("tweak"),
|
||||||
level = DeprecationLevel.ERROR)
|
level = DeprecationLevel.ERROR)
|
||||||
fun LedgerDslInterpreter<LastLineShouldTestForVerifiesOrFails, TransactionDslInterpreter<LastLineShouldTestForVerifiesOrFails>>.ledger(
|
@Suppress("UNUSED_PARAMETER")
|
||||||
dsl: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.() -> Unit) {
|
fun LedgerDSLInterpreter<LastLineShouldTestForVerifiesOrFails, TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>>.ledger(
|
||||||
this.toString()
|
dsl: LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit) {
|
||||||
dsl.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If you jumped here from a compiler error make sure the last line of your test tests for a transaction verify or fail
|
/**
|
||||||
* This is a dummy type that can only be instantiated by functions in this module. This way we can ensure that all tests
|
* If you jumped here from a compiler error make sure the last line of your test tests for a transaction verify or fail
|
||||||
* will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of
|
* This is a dummy type that can only be instantiated by functions in this module. This way we can ensure that all tests
|
||||||
* the triggered diagnostic
|
* will have as the last line either an accept or a failure test. The name is deliberately long to help make sense of
|
||||||
|
* the triggered diagnostic.
|
||||||
*/
|
*/
|
||||||
sealed class LastLineShouldTestForVerifiesOrFails {
|
sealed class LastLineShouldTestForVerifiesOrFails {
|
||||||
internal object Token: LastLineShouldTestForVerifiesOrFails()
|
internal object Token: LastLineShouldTestForVerifiesOrFails()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* that transactions corresponding to input states are not verified. Use [LedgerDsl.verifies] for that.
|
* that transactions corresponding to input states are not verified. Use [LedgerDSL.verifies] for that.
|
||||||
*/
|
*/
|
||||||
data class TestTransactionDslInterpreter(
|
data class TestTransactionDSLInterpreter(
|
||||||
override val ledgerInterpreter: TestLedgerDslInterpreter,
|
override val ledgerInterpreter: TestLedgerDSLInterpreter,
|
||||||
private val inputStateRefs: ArrayList<StateRef> = arrayListOf(),
|
private val inputStateRefs: ArrayList<StateRef> = arrayListOf(),
|
||||||
internal val outputStates: ArrayList<LabeledOutput> = arrayListOf(),
|
internal val outputStates: ArrayList<LabeledOutput> = arrayListOf(),
|
||||||
private val attachments: ArrayList<SecureHash> = arrayListOf(),
|
private val attachments: ArrayList<SecureHash> = arrayListOf(),
|
||||||
private val commands: ArrayList<Command> = arrayListOf(),
|
private val commands: ArrayList<Command> = arrayListOf(),
|
||||||
private val signers: LinkedHashSet<PublicKey> = LinkedHashSet(),
|
private val signers: LinkedHashSet<PublicKey> = LinkedHashSet(),
|
||||||
private val transactionType: TransactionType = TransactionType.General()
|
private val transactionType: TransactionType = TransactionType.General()
|
||||||
) : TransactionDslInterpreter<LastLineShouldTestForVerifiesOrFails>, OutputStateLookup by ledgerInterpreter {
|
) : TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>, OutputStateLookup by ledgerInterpreter {
|
||||||
private fun copy(): TestTransactionDslInterpreter =
|
private fun copy(): TestTransactionDSLInterpreter =
|
||||||
TestTransactionDslInterpreter(
|
TestTransactionDSLInterpreter(
|
||||||
ledgerInterpreter = ledgerInterpreter,
|
ledgerInterpreter = ledgerInterpreter,
|
||||||
inputStateRefs = ArrayList(inputStateRefs),
|
inputStateRefs = ArrayList(inputStateRefs),
|
||||||
outputStates = ArrayList(outputStates),
|
outputStates = ArrayList(outputStates),
|
||||||
@ -136,23 +155,23 @@ data class TestTransactionDslInterpreter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun tweak(
|
override fun tweak(
|
||||||
dsl: TransactionDsl<
|
dsl: TransactionDSL<
|
||||||
LastLineShouldTestForVerifiesOrFails,
|
LastLineShouldTestForVerifiesOrFails,
|
||||||
TransactionDslInterpreter<LastLineShouldTestForVerifiesOrFails>
|
TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>
|
||||||
>.() -> LastLineShouldTestForVerifiesOrFails
|
>.() -> LastLineShouldTestForVerifiesOrFails
|
||||||
) = dsl(TransactionDsl(copy()))
|
) = dsl(TransactionDSL(copy()))
|
||||||
}
|
}
|
||||||
|
|
||||||
class AttachmentResolutionException(attachmentId: SecureHash) :
|
class AttachmentResolutionException(attachmentId: SecureHash) :
|
||||||
Exception("Attachment with id $attachmentId not found")
|
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,
|
||||||
internal val labelToOutputStateAndRefs: HashMap<String, StateAndRef<ContractState>> = HashMap(),
|
internal val labelToOutputStateAndRefs: HashMap<String, StateAndRef<ContractState>> = HashMap(),
|
||||||
private val transactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap(),
|
private val transactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap(),
|
||||||
private val nonVerifiedTransactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
|
private val nonVerifiedTransactionWithLocations: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
|
||||||
) : LedgerDslInterpreter<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter> {
|
) : LedgerDSLInterpreter<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter> {
|
||||||
|
|
||||||
val wireTransactions: List<WireTransaction> get() = transactionWithLocations.values.map { it.transaction }
|
val wireTransactions: List<WireTransaction> get() = transactionWithLocations.values.map { it.transaction }
|
||||||
|
|
||||||
@ -178,8 +197,8 @@ data class TestLedgerDslInterpreter private constructor (
|
|||||||
class TypeMismatch(requested: Class<*>, actual: Class<*>) :
|
class TypeMismatch(requested: Class<*>, actual: Class<*>) :
|
||||||
Exception("Actual type $actual is not a subtype of requested type $requested")
|
Exception("Actual type $actual is not a subtype of requested type $requested")
|
||||||
|
|
||||||
internal fun copy(): TestLedgerDslInterpreter =
|
internal fun copy(): TestLedgerDSLInterpreter =
|
||||||
TestLedgerDslInterpreter(
|
TestLedgerDSLInterpreter(
|
||||||
identityService,
|
identityService,
|
||||||
storageService,
|
storageService,
|
||||||
labelToOutputStateAndRefs = HashMap(labelToOutputStateAndRefs),
|
labelToOutputStateAndRefs = HashMap(labelToOutputStateAndRefs),
|
||||||
@ -207,16 +226,16 @@ data class TestLedgerDslInterpreter private constructor (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun <reified State: ContractState> resolveStateRef(stateRef: StateRef): TransactionState<State> {
|
internal inline fun <reified S : ContractState> resolveStateRef(stateRef: StateRef): TransactionState<S> {
|
||||||
val transactionWithLocation =
|
val transactionWithLocation =
|
||||||
transactionWithLocations[stateRef.txhash] ?:
|
transactionWithLocations[stateRef.txhash] ?:
|
||||||
nonVerifiedTransactionWithLocations[stateRef.txhash] ?:
|
nonVerifiedTransactionWithLocations[stateRef.txhash] ?:
|
||||||
throw TransactionResolutionException(stateRef.txhash)
|
throw TransactionResolutionException(stateRef.txhash)
|
||||||
val output = transactionWithLocation.transaction.outputs[stateRef.index]
|
val output = transactionWithLocation.transaction.outputs[stateRef.index]
|
||||||
return if (State::class.java.isAssignableFrom(output.data.javaClass)) @Suppress("UNCHECKED_CAST") {
|
return if (S::class.java.isAssignableFrom(output.data.javaClass)) @Suppress("UNCHECKED_CAST") {
|
||||||
output as TransactionState<State>
|
output as TransactionState<S>
|
||||||
} else {
|
} else {
|
||||||
throw TypeMismatch(requested = State::class.java, actual = output.data.javaClass)
|
throw TypeMismatch(requested = S::class.java, actual = output.data.javaClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,10 +243,10 @@ data class TestLedgerDslInterpreter private constructor (
|
|||||||
storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
|
storageService.attachments.openAttachment(attachmentId) ?: throw AttachmentResolutionException(attachmentId)
|
||||||
|
|
||||||
private fun <Return> interpretTransactionDsl(
|
private fun <Return> interpretTransactionDsl(
|
||||||
dsl: TransactionDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter>.() -> Return
|
dsl: TransactionDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter>.() -> Return
|
||||||
): TestTransactionDslInterpreter {
|
): TestTransactionDSLInterpreter {
|
||||||
val transactionInterpreter = TestTransactionDslInterpreter(this)
|
val transactionInterpreter = TestTransactionDSLInterpreter(this)
|
||||||
dsl(TransactionDsl(transactionInterpreter))
|
dsl(TransactionDSL(transactionInterpreter))
|
||||||
return transactionInterpreter
|
return transactionInterpreter
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,9 +272,9 @@ data class TestLedgerDslInterpreter private constructor (
|
|||||||
fun outputToLabel(state: ContractState): String? =
|
fun outputToLabel(state: ContractState): String? =
|
||||||
labelToOutputStateAndRefs.filter { it.value.state.data == state }.keys.firstOrNull()
|
labelToOutputStateAndRefs.filter { it.value.state.data == state }.keys.firstOrNull()
|
||||||
|
|
||||||
private fun <Return> recordTransactionWithTransactionMap(
|
private fun <R> recordTransactionWithTransactionMap(
|
||||||
transactionLabel: String?,
|
transactionLabel: String?,
|
||||||
dsl: TransactionDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter>.() -> Return,
|
dsl: TransactionDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter>.() -> R,
|
||||||
transactionMap: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
|
transactionMap: HashMap<SecureHash, WireTransactionWithLocation> = HashMap()
|
||||||
): WireTransaction {
|
): WireTransaction {
|
||||||
val transactionLocation = getCallerLocation(3)
|
val transactionLocation = getCallerLocation(3)
|
||||||
@ -277,19 +296,18 @@ data class TestLedgerDslInterpreter private constructor (
|
|||||||
|
|
||||||
override fun transaction(
|
override fun transaction(
|
||||||
transactionLabel: String?,
|
transactionLabel: String?,
|
||||||
dsl: TransactionDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter>.() -> LastLineShouldTestForVerifiesOrFails
|
dsl: TransactionDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter>.() -> LastLineShouldTestForVerifiesOrFails
|
||||||
) = recordTransactionWithTransactionMap(transactionLabel, dsl, transactionWithLocations)
|
) = recordTransactionWithTransactionMap(transactionLabel, dsl, transactionWithLocations)
|
||||||
|
|
||||||
override fun nonVerifiedTransaction(
|
override fun unverifiedTransaction(
|
||||||
transactionLabel: String?,
|
transactionLabel: String?,
|
||||||
dsl: TransactionDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter>.() -> Unit
|
dsl: TransactionDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter>.() -> Unit
|
||||||
) =
|
) = recordTransactionWithTransactionMap(transactionLabel, dsl, nonVerifiedTransactionWithLocations)
|
||||||
recordTransactionWithTransactionMap(transactionLabel, dsl, nonVerifiedTransactionWithLocations)
|
|
||||||
|
|
||||||
override fun tweak(
|
override fun tweak(
|
||||||
dsl: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter,
|
dsl: LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter,
|
||||||
LedgerDslInterpreter<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter>>.() -> Unit) =
|
LedgerDSLInterpreter<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter>>.() -> Unit) =
|
||||||
dsl(LedgerDsl(copy()))
|
dsl(LedgerDSL(copy()))
|
||||||
|
|
||||||
override fun attachment(attachment: InputStream): SecureHash {
|
override fun attachment(attachment: InputStream): SecureHash {
|
||||||
return storageService.attachments.importAttachment(attachment)
|
return storageService.attachments.importAttachment(attachment)
|
||||||
@ -304,7 +322,7 @@ data class TestLedgerDslInterpreter private constructor (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <State: ContractState> retrieveOutputStateAndRef(clazz: Class<State>, label: String): StateAndRef<State> {
|
override fun <S : ContractState> retrieveOutputStateAndRef(clazz: Class<S>, label: String): StateAndRef<S> {
|
||||||
val stateAndRef = labelToOutputStateAndRefs[label]
|
val stateAndRef = labelToOutputStateAndRefs[label]
|
||||||
if (stateAndRef == null) {
|
if (stateAndRef == null) {
|
||||||
throw IllegalArgumentException("State with label '$label' was not found")
|
throw IllegalArgumentException("State with label '$label' was not found")
|
||||||
@ -312,50 +330,25 @@ data class TestLedgerDslInterpreter private constructor (
|
|||||||
throw TypeMismatch(requested = clazz, actual = stateAndRef.state.data.javaClass)
|
throw TypeMismatch(requested = clazz, actual = stateAndRef.state.data.javaClass)
|
||||||
} else {
|
} else {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
return stateAndRef as StateAndRef<State>
|
return stateAndRef as StateAndRef<S>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun signAll(transactionsToSign: List<WireTransaction>, vararg extraKeys: KeyPair): List<SignedTransaction> {
|
fun signAll(transactionsToSign: List<WireTransaction>, extraKeys: Array<out KeyPair>) = transactionsToSign.map { wtx ->
|
||||||
return transactionsToSign.map { wtx ->
|
val allPubKeys = wtx.signers.toMutableSet()
|
||||||
val allPubKeys = wtx.signers.toMutableSet()
|
val bits = wtx.serialize()
|
||||||
val bits = wtx.serialize()
|
require(bits == wtx.serialized)
|
||||||
require(bits == wtx.serialized)
|
val signatures = ArrayList<DigitalSignature.WithKey>()
|
||||||
val signatures = ArrayList<DigitalSignature.WithKey>()
|
for (key in ALL_TEST_KEYS + extraKeys) {
|
||||||
for (key in ALL_TEST_KEYS + extraKeys) {
|
if (allPubKeys.contains(key.public)) {
|
||||||
if (allPubKeys.contains(key.public)) {
|
signatures += key.signWithECDSA(bits)
|
||||||
signatures += key.signWithECDSA(bits)
|
allPubKeys -= key.public
|
||||||
allPubKeys -= key.public
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SignedTransaction(bits, signatures)
|
|
||||||
}
|
}
|
||||||
|
SignedTransaction(bits, signatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.signAll(
|
||||||
ledger {
|
transactionsToSign: List<WireTransaction> = this.interpreter.wireTransactions, vararg extraKeys: KeyPair) =
|
||||||
nonVerifiedTransaction {
|
signAll(transactionsToSign, extraKeys)
|
||||||
output("hello") { DummyLinearState() }
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction {
|
|
||||||
input("hello")
|
|
||||||
tweak {
|
|
||||||
timestamp(TEST_TX_TIME, MEGA_CORP_PUBKEY)
|
|
||||||
fails()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tweak {
|
|
||||||
|
|
||||||
transaction {
|
|
||||||
input("hello")
|
|
||||||
timestamp(TEST_TX_TIME, MEGA_CORP_PUBKEY)
|
|
||||||
fails()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.verifies()
|
|
||||||
}
|
|
||||||
}
|
|
@ -93,13 +93,20 @@ object JavaTestHelpers {
|
|||||||
@JvmStatic @JvmOverloads fun ledger(
|
@JvmStatic @JvmOverloads fun ledger(
|
||||||
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
identityService: IdentityService = MOCK_IDENTITY_SERVICE,
|
||||||
storageService: StorageService = MockStorageService(),
|
storageService: StorageService = MockStorageService(),
|
||||||
dsl: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.() -> Unit
|
dsl: LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.() -> Unit
|
||||||
): LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter> {
|
): LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter> {
|
||||||
val ledgerDsl = LedgerDsl(TestLedgerDslInterpreter(identityService, storageService))
|
val ledgerDsl = LedgerDSL(TestLedgerDSLInterpreter(identityService, storageService))
|
||||||
dsl(ledgerDsl)
|
dsl(ledgerDsl)
|
||||||
return ledgerDsl
|
return ledgerDsl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic @JvmOverloads fun transaction(
|
||||||
|
transactionLabel: String? = null,
|
||||||
|
dsl: TransactionDSL<
|
||||||
|
LastLineShouldTestForVerifiesOrFails,
|
||||||
|
TransactionDSLInterpreter<LastLineShouldTestForVerifiesOrFails>
|
||||||
|
>.() -> LastLineShouldTestForVerifiesOrFails
|
||||||
|
) = ledger { transaction(transactionLabel, dsl) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val TEST_TX_TIME = JavaTestHelpers.TEST_TX_TIME
|
val TEST_TX_TIME = JavaTestHelpers.TEST_TX_TIME
|
||||||
|
@ -30,39 +30,36 @@ import java.time.Instant
|
|||||||
//
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The [TransactionDslInterpreter] defines the interface DSL interpreters should satisfy. No
|
* The [TransactionDSLInterpreter] defines the interface DSL interpreters should satisfy. No
|
||||||
* overloading/default valuing should be done here, only the basic functions that are required to implement everything.
|
* overloading/default valuing should be done here, only the basic functions that are required to implement everything.
|
||||||
* Same goes for functions requiring reflection e.g. [OutputStateLookup.retrieveOutputStateAndRef]
|
* Same goes for functions requiring reflection e.g. [OutputStateLookup.retrieveOutputStateAndRef]
|
||||||
* Put convenience functions in [TransactionDsl] instead. There are some cases where the overloads would clash with the
|
* 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]).
|
* 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
|
* This way the responsibility of providing a nice frontend DSL and the implementation(s) are separated
|
||||||
*/
|
*/
|
||||||
interface TransactionDslInterpreter<Return> : OutputStateLookup {
|
interface TransactionDSLInterpreter<R> : OutputStateLookup {
|
||||||
val ledgerInterpreter: LedgerDslInterpreter<Return, TransactionDslInterpreter<Return>>
|
val ledgerInterpreter: LedgerDSLInterpreter<R, TransactionDSLInterpreter<R>>
|
||||||
fun input(stateRef: StateRef)
|
fun input(stateRef: StateRef)
|
||||||
fun _output(label: String?, notary: Party, contractState: ContractState)
|
fun _output(label: String?, notary: Party, contractState: ContractState)
|
||||||
fun attachment(attachmentId: SecureHash)
|
fun attachment(attachmentId: SecureHash)
|
||||||
fun _command(signers: List<PublicKey>, commandData: CommandData)
|
fun _command(signers: List<PublicKey>, commandData: CommandData)
|
||||||
fun verifies(): Return
|
fun verifies(): R
|
||||||
fun failsWith(expectedMessage: String?): Return
|
fun failsWith(expectedMessage: String?): R
|
||||||
fun tweak(
|
fun tweak(
|
||||||
dsl: TransactionDsl<Return, TransactionDslInterpreter<Return>>.() -> Return
|
dsl: TransactionDSL<R, TransactionDSLInterpreter<R>>.() -> R
|
||||||
): Return
|
): R
|
||||||
}
|
}
|
||||||
|
|
||||||
class TransactionDsl<
|
class TransactionDSL<R, out T : TransactionDSLInterpreter<R>> (val interpreter: T) :
|
||||||
Return,
|
TransactionDSLInterpreter<R> by interpreter {
|
||||||
out TransactionInterpreter: TransactionDslInterpreter<Return>
|
|
||||||
> (val interpreter: TransactionInterpreter)
|
|
||||||
: TransactionDslInterpreter<Return> by interpreter {
|
|
||||||
|
|
||||||
fun input(stateLabel: String) = input(retrieveOutputStateAndRef(ContractState::class.java, stateLabel).ref)
|
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
|
* Adds the passed in state as a non-verified transaction output to the ledger and adds that as an input
|
||||||
*/
|
*/
|
||||||
fun input(state: ContractState) {
|
fun input(state: ContractState) {
|
||||||
val transaction = ledgerInterpreter.nonVerifiedTransaction(null) {
|
val transaction = ledgerInterpreter.unverifiedTransaction(null) {
|
||||||
output { state }
|
output { state }
|
||||||
}
|
}
|
||||||
input(transaction.outRef<ContractState>(0).ref)
|
input(transaction.outRef<ContractState>(0).ref)
|
@ -48,7 +48,7 @@ class TransactionGroupTests {
|
|||||||
@Test
|
@Test
|
||||||
fun success() {
|
fun success() {
|
||||||
ledger {
|
ledger {
|
||||||
nonVerifiedTransaction {
|
unverifiedTransaction {
|
||||||
output("£1000") { A_THOUSAND_POUNDS }
|
output("£1000") { A_THOUSAND_POUNDS }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ class TransactionGroupTests {
|
|||||||
fun duplicatedInputs() {
|
fun duplicatedInputs() {
|
||||||
// Check that a transaction cannot refer to the same input more than once.
|
// Check that a transaction cannot refer to the same input more than once.
|
||||||
ledger {
|
ledger {
|
||||||
nonVerifiedTransaction {
|
unverifiedTransaction {
|
||||||
output("£1000") { A_THOUSAND_POUNDS }
|
output("£1000") { A_THOUSAND_POUNDS }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ class TransactionGroupTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun signGroup() {
|
fun signGroup() {
|
||||||
val signedTxns: List<SignedTransaction> = ledger {
|
ledger {
|
||||||
transaction {
|
transaction {
|
||||||
output("£1000") { A_THOUSAND_POUNDS }
|
output("£1000") { A_THOUSAND_POUNDS }
|
||||||
command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
|
command(MINI_CORP_PUBKEY) { TestCash.Commands.Issue() }
|
||||||
@ -182,12 +182,14 @@ class TransactionGroupTests {
|
|||||||
command(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) }
|
command(MINI_CORP_PUBKEY) { TestCash.Commands.Exit(1000.POUNDS) }
|
||||||
this.verifies()
|
this.verifies()
|
||||||
}
|
}
|
||||||
}.interpreter.wireTransactions.let { signAll(it) }
|
|
||||||
|
|
||||||
// Now go through the conversion -> verification path with them.
|
val signedTxns: List<SignedTransaction> = signAll()
|
||||||
val ltxns = signedTxns.map {
|
|
||||||
it.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE, MockStorageService().attachments)
|
// Now go through the conversion -> verification path with them.
|
||||||
}.toSet()
|
val ltxns = signedTxns.map {
|
||||||
TransactionGroup(ltxns, emptySet()).verify()
|
it.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE, MockStorageService().attachments)
|
||||||
|
}.toSet()
|
||||||
|
TransactionGroup(ltxns, emptySet()).verify()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import com.r3corda.core.node.services.ServiceType
|
|||||||
import com.r3corda.core.node.services.TransactionStorage
|
import com.r3corda.core.node.services.TransactionStorage
|
||||||
import com.r3corda.core.node.services.Wallet
|
import com.r3corda.core.node.services.Wallet
|
||||||
import com.r3corda.core.random63BitValue
|
import com.r3corda.core.random63BitValue
|
||||||
import com.r3corda.core.contracts.Attachment
|
|
||||||
import com.r3corda.core.seconds
|
import com.r3corda.core.seconds
|
||||||
import com.r3corda.core.testing.*
|
import com.r3corda.core.testing.*
|
||||||
import com.r3corda.core.utilities.BriefLogFormatter
|
import com.r3corda.core.utilities.BriefLogFormatter
|
||||||
@ -367,7 +366,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.runWithError(
|
private fun LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.runWithError(
|
||||||
bobError: Boolean,
|
bobError: Boolean,
|
||||||
aliceError: Boolean,
|
aliceError: Boolean,
|
||||||
expectedMessageSubstring: String
|
expectedMessageSubstring: String
|
||||||
@ -423,7 +422,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
wtxToSign: List<WireTransaction>,
|
wtxToSign: List<WireTransaction>,
|
||||||
services: ServiceHub,
|
services: ServiceHub,
|
||||||
vararg extraKeys: KeyPair): Map<SecureHash, SignedTransaction> {
|
vararg extraKeys: KeyPair): Map<SecureHash, SignedTransaction> {
|
||||||
val signed: List<SignedTransaction> = signAll(wtxToSign, *extraKeys)
|
val signed: List<SignedTransaction> = signAll(wtxToSign, extraKeys)
|
||||||
services.recordTransactions(signed)
|
services.recordTransactions(signed)
|
||||||
val validatedTransactions = services.storageService.validatedTransactions
|
val validatedTransactions = services.storageService.validatedTransactions
|
||||||
if (validatedTransactions is RecordingTransactionStorage) {
|
if (validatedTransactions is RecordingTransactionStorage) {
|
||||||
@ -432,7 +431,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
return signed.associateBy { it.id }
|
return signed.associateBy { it.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.fillUpForBuyer(
|
private fun LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForBuyer(
|
||||||
withError: Boolean,
|
withError: Boolean,
|
||||||
owner: PublicKey = BOB_PUBKEY,
|
owner: PublicKey = BOB_PUBKEY,
|
||||||
issuer: PartyAndReference = MEGA_CORP.ref(1)): Pair<Wallet, List<WireTransaction>> {
|
issuer: PartyAndReference = MEGA_CORP.ref(1)): Pair<Wallet, List<WireTransaction>> {
|
||||||
@ -473,7 +472,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
return Pair(wallet, listOf(eb1, bc1, bc2))
|
return Pair(wallet, listOf(eb1, bc1, bc2))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>.fillUpForSeller(
|
private fun LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>.fillUpForSeller(
|
||||||
withError: Boolean,
|
withError: Boolean,
|
||||||
owner: PublicKey,
|
owner: PublicKey,
|
||||||
amount: Amount<Issued<Currency>>,
|
amount: Amount<Issued<Currency>>,
|
||||||
|
@ -9,7 +9,7 @@ import org.graphstream.graph.Node
|
|||||||
import org.graphstream.graph.implementations.SingleGraph
|
import org.graphstream.graph.implementations.SingleGraph
|
||||||
import kotlin.reflect.memberProperties
|
import kotlin.reflect.memberProperties
|
||||||
|
|
||||||
class GraphVisualiser(val dsl: LedgerDsl<LastLineShouldTestForVerifiesOrFails, TestTransactionDslInterpreter, TestLedgerDslInterpreter>) {
|
class GraphVisualiser(val dsl: LedgerDSL<LastLineShouldTestForVerifiesOrFails, TestTransactionDSLInterpreter, TestLedgerDSLInterpreter>) {
|
||||||
companion object {
|
companion object {
|
||||||
val css = GraphVisualiser::class.java.getResourceAsStream("graph.css").bufferedReader().readText()
|
val css = GraphVisualiser::class.java.getResourceAsStream("graph.css").bufferedReader().readText()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user