mirror of
https://github.com/corda/corda.git
synced 2025-06-22 17:09:00 +00:00
contracts, node: Port CommercialPaperTests, TwoPartyTradeProtocolTests and GroupToGraphConversion to use new dsl
This commit is contained in:
@ -17,10 +17,10 @@ import kotlin.test.assertFailsWith
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
interface ICommercialPaperTestTemplate {
|
interface ICommercialPaperTestTemplate {
|
||||||
open fun getPaper(): ICommercialPaperState
|
fun getPaper(): ICommercialPaperState
|
||||||
open fun getIssueCommand(): CommandData
|
fun getIssueCommand(): CommandData
|
||||||
open fun getRedeemCommand(): CommandData
|
fun getRedeemCommand(): CommandData
|
||||||
open fun getMoveCommand(): CommandData
|
fun getMoveCommand(): CommandData
|
||||||
}
|
}
|
||||||
|
|
||||||
class JavaCommercialPaperTest() : ICommercialPaperTestTemplate {
|
class JavaCommercialPaperTest() : ICommercialPaperTestTemplate {
|
||||||
@ -63,79 +63,120 @@ class CommercialPaperTestsGeneric {
|
|||||||
val issuer = MEGA_CORP.ref(123)
|
val issuer = MEGA_CORP.ref(123)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun ok() {
|
fun `trade lifecycle test`() {
|
||||||
trade().verify()
|
val someProfits = 1200.DOLLARS `issued by` issuer
|
||||||
|
ledger {
|
||||||
|
nonVerifiedTransaction {
|
||||||
|
output("alice's $900", 900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY)
|
||||||
|
output("some profits", someProfits.STATE `owned by` MEGA_CORP_PUBKEY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// Some CP is issued onto the ledger by MegaCorp.
|
||||||
fun `not matured at redemption`() {
|
transaction("Issuance") {
|
||||||
trade(redemptionTime = TEST_TX_TIME + 2.days).expectFailureOfTx(3, "must have matured")
|
output("paper") { thisTest.getPaper() }
|
||||||
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The CP is sold to alice for her $900, $100 less than the face value. At 10% interest after only 7 days,
|
||||||
|
// that sounds a bit too good to be true!
|
||||||
|
transaction("Trade") {
|
||||||
|
input("paper")
|
||||||
|
input("alice's $900")
|
||||||
|
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||||
|
output("alice's paper") { "paper".output<ICommercialPaperState>().data `owned by` ALICE_PUBKEY }
|
||||||
|
command(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||||
|
command(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time passes, and Alice redeem's her CP for $1000, netting a $100 profit. MegaCorp has received $1200
|
||||||
|
// as a single payment from somewhere and uses it to pay Alice off, keeping the remaining $200 as change.
|
||||||
|
transaction("Redemption") {
|
||||||
|
input("alice's paper")
|
||||||
|
input("some profits")
|
||||||
|
|
||||||
|
fun TransactionDsl<TransactionDslInterpreter>.outputs(aliceGetsBack: Amount<Issued<Currency>>) {
|
||||||
|
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY }
|
||||||
|
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY }
|
||||||
|
}
|
||||||
|
|
||||||
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
|
command(ALICE_PUBKEY) { thisTest.getRedeemCommand() }
|
||||||
|
|
||||||
|
tweak {
|
||||||
|
outputs(700.DOLLARS `issued by` issuer)
|
||||||
|
timestamp(TEST_TX_TIME + 8.days)
|
||||||
|
this `fails with` "received amount equals the face value"
|
||||||
|
}
|
||||||
|
outputs(1000.DOLLARS `issued by` issuer)
|
||||||
|
|
||||||
|
|
||||||
|
tweak {
|
||||||
|
timestamp(TEST_TX_TIME + 2.days)
|
||||||
|
this `fails with` "must have matured"
|
||||||
|
}
|
||||||
|
timestamp(TEST_TX_TIME + 8.days)
|
||||||
|
|
||||||
|
tweak {
|
||||||
|
output { "paper".output<ICommercialPaperState>().data }
|
||||||
|
this `fails with` "must be destroyed"
|
||||||
|
}
|
||||||
|
|
||||||
|
verifies()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `key mismatch at issue`() {
|
fun `key mismatch at issue`() {
|
||||||
transactionGroup {
|
ledger {
|
||||||
transaction {
|
transaction {
|
||||||
output { thisTest.getPaper() }
|
output { thisTest.getPaper() }
|
||||||
arg(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"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectFailureOfTx(1, "signed by the claimed issuer")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `face value is not zero`() {
|
fun `face value is not zero`() {
|
||||||
transactionGroup {
|
ledger {
|
||||||
transaction {
|
transaction {
|
||||||
output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
|
output { thisTest.getPaper().withFaceValue(0.DOLLARS `issued by` issuer) }
|
||||||
arg(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"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectFailureOfTx(1, "face value is not zero")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `maturity date not in the past`() {
|
fun `maturity date not in the past`() {
|
||||||
transactionGroup {
|
ledger {
|
||||||
transaction {
|
transaction {
|
||||||
output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
|
output { thisTest.getPaper().withMaturityDate(TEST_TX_TIME - 10.days) }
|
||||||
arg(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"
|
||||||
}
|
}
|
||||||
|
|
||||||
expectFailureOfTx(1, "maturity date is not in the past")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `issue cannot replace an existing state`() {
|
fun `issue cannot replace an existing state`() {
|
||||||
transactionGroup {
|
ledger {
|
||||||
roots {
|
nonVerifiedTransaction {
|
||||||
transaction(thisTest.getPaper() `with notary` DUMMY_NOTARY label "paper")
|
output("paper") { thisTest.getPaper() }
|
||||||
}
|
}
|
||||||
transaction {
|
transaction {
|
||||||
input("paper")
|
input("paper")
|
||||||
output { thisTest.getPaper() }
|
output { thisTest.getPaper() }
|
||||||
arg(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
command(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
}
|
this `fails with` "there is no input state"
|
||||||
|
|
||||||
expectFailureOfTx(1, "there is no input state")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `did not receive enough money at redemption`() {
|
|
||||||
trade(aliceGetsBack = 700.DOLLARS `issued by` issuer).expectFailureOfTx(3, "received amount equals the face value")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `paper must be destroyed by redemption`() {
|
|
||||||
trade(destroyPaperAtRedemption = false).expectFailureOfTx(3, "must be destroyed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : ContractState> cashOutputsToWallet(vararg outputs: TransactionState<T>): Pair<LedgerTransaction, List<StateAndRef<T>>> {
|
fun <T : ContractState> cashOutputsToWallet(vararg outputs: TransactionState<T>): Pair<LedgerTransaction, List<StateAndRef<T>>> {
|
||||||
@ -199,52 +240,4 @@ class CommercialPaperTestsGeneric {
|
|||||||
|
|
||||||
TransactionGroup(setOf(issueTX, moveTX, validRedemption), setOf(corpWalletTX, alicesWalletTX)).verify()
|
TransactionGroup(setOf(issueTX, moveTX, validRedemption), setOf(corpWalletTX, alicesWalletTX)).verify()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a trade lifecycle with various parameters.
|
|
||||||
fun trade(redemptionTime: Instant = TEST_TX_TIME + 8.days,
|
|
||||||
aliceGetsBack: Amount<Issued<Currency>> = 1000.DOLLARS `issued by` issuer,
|
|
||||||
destroyPaperAtRedemption: Boolean = true): TransactionGroupDSL<ICommercialPaperState> {
|
|
||||||
val someProfits = 1200.DOLLARS `issued by` issuer
|
|
||||||
return transactionGroupFor() {
|
|
||||||
roots {
|
|
||||||
transaction(900.DOLLARS.CASH `issued by` issuer `owned by` ALICE_PUBKEY `with notary` DUMMY_NOTARY label "alice's $900")
|
|
||||||
transaction(someProfits.STATE `owned by` MEGA_CORP_PUBKEY `with notary` DUMMY_NOTARY label "some profits")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some CP is issued onto the ledger by MegaCorp.
|
|
||||||
transaction("Issuance") {
|
|
||||||
output("paper") { thisTest.getPaper() }
|
|
||||||
arg(MEGA_CORP_PUBKEY) { thisTest.getIssueCommand() }
|
|
||||||
timestamp(TEST_TX_TIME)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The CP is sold to alice for her $900, $100 less than the face value. At 10% interest after only 7 days,
|
|
||||||
// that sounds a bit too good to be true!
|
|
||||||
transaction("Trade") {
|
|
||||||
input("paper")
|
|
||||||
input("alice's $900")
|
|
||||||
output("borrowed $900") { 900.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
|
||||||
output("alice's paper") { "paper".output.data `owned by` ALICE_PUBKEY }
|
|
||||||
arg(ALICE_PUBKEY) { Cash.Commands.Move() }
|
|
||||||
arg(MEGA_CORP_PUBKEY) { thisTest.getMoveCommand() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time passes, and Alice redeem's her CP for $1000, netting a $100 profit. MegaCorp has received $1200
|
|
||||||
// as a single payment from somewhere and uses it to pay Alice off, keeping the remaining $200 as change.
|
|
||||||
transaction("Redemption") {
|
|
||||||
input("alice's paper")
|
|
||||||
input("some profits")
|
|
||||||
|
|
||||||
output("Alice's profit") { aliceGetsBack.STATE `owned by` ALICE_PUBKEY }
|
|
||||||
output("Change") { (someProfits - aliceGetsBack).STATE `owned by` MEGA_CORP_PUBKEY }
|
|
||||||
if (!destroyPaperAtRedemption)
|
|
||||||
output { "paper".output.data }
|
|
||||||
|
|
||||||
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
|
||||||
arg(ALICE_PUBKEY) { thisTest.getRedeemCommand() }
|
|
||||||
|
|
||||||
timestamp(redemptionTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,9 @@ class TwoPartyTradeProtocolTests {
|
|||||||
// we run in the unit test thread exclusively to speed things up, ensure deterministic results and
|
// we run in the unit test thread exclusively to speed things up, ensure deterministic results and
|
||||||
// allow interruption half way through.
|
// allow interruption half way through.
|
||||||
net = MockNetwork(false, true)
|
net = MockNetwork(false, true)
|
||||||
transactionGroupFor<ContractState> {
|
|
||||||
|
ledger {
|
||||||
|
|
||||||
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||||
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
|
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
|
||||||
val bobNode = net.createPartyNode(notaryNode.info, BOB.name, BOB_KEY)
|
val bobNode = net.createPartyNode(notaryNode.info, BOB.name, BOB_KEY)
|
||||||
@ -113,7 +115,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
aliceNode.smm,
|
aliceNode.smm,
|
||||||
notaryNode.info,
|
notaryNode.info,
|
||||||
bobNode.info.identity,
|
bobNode.info.identity,
|
||||||
lookup("alice's paper"),
|
"alice's paper".outputStateAndRef(),
|
||||||
1000.DOLLARS `issued by` issuer,
|
1000.DOLLARS `issued by` issuer,
|
||||||
ALICE_KEY,
|
ALICE_KEY,
|
||||||
buyerSessionID
|
buyerSessionID
|
||||||
@ -133,7 +135,8 @@ class TwoPartyTradeProtocolTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `shutdown and restore`() {
|
fun `shutdown and restore`() {
|
||||||
transactionGroupFor<ContractState> {
|
|
||||||
|
ledger {
|
||||||
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||||
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
|
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
|
||||||
var bobNode = net.createPartyNode(notaryNode.info, BOB.name, BOB_KEY)
|
var bobNode = net.createPartyNode(notaryNode.info, BOB.name, BOB_KEY)
|
||||||
@ -155,7 +158,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
aliceNode.smm,
|
aliceNode.smm,
|
||||||
notaryNode.info,
|
notaryNode.info,
|
||||||
bobNode.info.identity,
|
bobNode.info.identity,
|
||||||
lookup("alice's paper"),
|
"alice's paper".outputStateAndRef(),
|
||||||
1000.DOLLARS `issued by` issuer,
|
1000.DOLLARS `issued by` issuer,
|
||||||
ALICE_KEY,
|
ALICE_KEY,
|
||||||
buyerSessionID
|
buyerSessionID
|
||||||
@ -246,7 +249,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `check dependencies of sale asset are resolved`() {
|
fun `check dependencies of sale asset are resolved`() {
|
||||||
transactionGroupFor<ContractState> {
|
ledger {
|
||||||
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||||
val aliceNode = makeNodeWithTracking(notaryNode.info, ALICE.name, ALICE_KEY)
|
val aliceNode = makeNodeWithTracking(notaryNode.info, ALICE.name, ALICE_KEY)
|
||||||
val bobNode = makeNodeWithTracking(notaryNode.info, BOB.name, BOB_KEY)
|
val bobNode = makeNodeWithTracking(notaryNode.info, BOB.name, BOB_KEY)
|
||||||
@ -275,7 +278,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
aliceNode.smm,
|
aliceNode.smm,
|
||||||
notaryNode.info,
|
notaryNode.info,
|
||||||
bobNode.info.identity,
|
bobNode.info.identity,
|
||||||
lookup("alice's paper"),
|
"alice's paper".outputStateAndRef(),
|
||||||
1000.DOLLARS `issued by` issuer,
|
1000.DOLLARS `issued by` issuer,
|
||||||
ALICE_KEY,
|
ALICE_KEY,
|
||||||
buyerSessionID
|
buyerSessionID
|
||||||
@ -350,19 +353,19 @@ class TwoPartyTradeProtocolTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `dependency with error on buyer side`() {
|
fun `dependency with error on buyer side`() {
|
||||||
transactionGroupFor<ContractState> {
|
ledger {
|
||||||
runWithError(true, false, "at least one asset input")
|
runWithError(true, false, "at least one asset input")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `dependency with error on seller side`() {
|
fun `dependency with error on seller side`() {
|
||||||
transactionGroupFor<ContractState> {
|
ledger {
|
||||||
runWithError(false, true, "must be timestamped")
|
runWithError(false, true, "must be timestamped")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TransactionGroupDSL<ContractState>.runWithError(bobError: Boolean, aliceError: Boolean,
|
private fun LedgerDsl<TransactionDslInterpreter, LedgerDslInterpreter<TransactionDslInterpreter>>.runWithError(bobError: Boolean, aliceError: Boolean,
|
||||||
expectedMessageSubstring: String) {
|
expectedMessageSubstring: String) {
|
||||||
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
val notaryNode = net.createNotaryNode(DUMMY_NOTARY.name, DUMMY_NOTARY_KEY)
|
||||||
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
|
val aliceNode = net.createPartyNode(notaryNode.info, ALICE.name, ALICE_KEY)
|
||||||
@ -385,7 +388,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
aliceNode.smm,
|
aliceNode.smm,
|
||||||
notaryNode.info,
|
notaryNode.info,
|
||||||
bobNode.info.identity,
|
bobNode.info.identity,
|
||||||
lookup("alice's paper"),
|
"alice's paper".outputStateAndRef(),
|
||||||
1000.DOLLARS `issued by` issuer,
|
1000.DOLLARS `issued by` issuer,
|
||||||
ALICE_KEY,
|
ALICE_KEY,
|
||||||
buyerSessionID
|
buyerSessionID
|
||||||
@ -411,7 +414,8 @@ class TwoPartyTradeProtocolTests {
|
|||||||
assertTrue(e.cause!!.cause!!.message!!.contains(expectedMessageSubstring))
|
assertTrue(e.cause!!.cause!!.message!!.contains(expectedMessageSubstring))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TransactionGroupDSL<ContractState>.insertFakeTransactions(wtxToSign: List<WireTransaction>,
|
private fun insertFakeTransactions(
|
||||||
|
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)
|
||||||
@ -423,7 +427,8 @@ class TwoPartyTradeProtocolTests {
|
|||||||
return signed.associateBy { it.id }
|
return signed.associateBy { it.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TransactionGroupDSL<ContractState>.fillUpForBuyer(withError: Boolean,
|
private fun LedgerDsl<TransactionDslInterpreter, LedgerDslInterpreter<TransactionDslInterpreter>>.fillUpForBuyer(
|
||||||
|
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>> {
|
||||||
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
// Bob (Buyer) has some cash he got from the Bank of Elbonia, Alice (Seller) has some commercial paper she
|
||||||
@ -434,7 +439,7 @@ class TwoPartyTradeProtocolTests {
|
|||||||
output("elbonian money 1") { 800.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
output("elbonian money 1") { 800.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||||
output("elbonian money 2") { 1000.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
output("elbonian money 2") { 1000.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY }
|
||||||
if (!withError)
|
if (!withError)
|
||||||
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Issue() }
|
||||||
timestamp(TEST_TX_TIME)
|
timestamp(TEST_TX_TIME)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,21 +447,22 @@ class TwoPartyTradeProtocolTests {
|
|||||||
val bc1 = transaction {
|
val bc1 = transaction {
|
||||||
input("elbonian money 1")
|
input("elbonian money 1")
|
||||||
output("bob cash 1") { 800.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
output("bob cash 1") { 800.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
||||||
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
}
|
}
|
||||||
|
|
||||||
val bc2 = transaction {
|
val bc2 = transaction {
|
||||||
input("elbonian money 2")
|
input("elbonian money 2")
|
||||||
output("bob cash 2") { 300.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
output("bob cash 2") { 300.DOLLARS.CASH `issued by` issuer `owned by` owner }
|
||||||
output { 700.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } // Change output.
|
output { 700.DOLLARS.CASH `issued by` issuer `owned by` MEGA_CORP_PUBKEY } // Change output.
|
||||||
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
command(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
}
|
}
|
||||||
|
|
||||||
val wallet = Wallet(listOf<StateAndRef<Cash.State>>(lookup("bob cash 1"), lookup("bob cash 2")))
|
val wallet = Wallet(listOf("bob cash 1".outputStateAndRef(), "bob cash 2".outputStateAndRef()))
|
||||||
return Pair(wallet, listOf(eb1, bc1, bc2))
|
return Pair(wallet, listOf(eb1, bc1, bc2))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TransactionGroupDSL<ContractState>.fillUpForSeller(withError: Boolean,
|
private fun LedgerDsl<TransactionDslInterpreter, LedgerDslInterpreter<TransactionDslInterpreter>>.fillUpForSeller(
|
||||||
|
withError: Boolean,
|
||||||
owner: PublicKey,
|
owner: PublicKey,
|
||||||
amount: Amount<Issued<Currency>>,
|
amount: Amount<Issued<Currency>>,
|
||||||
notary: Party,
|
notary: Party,
|
||||||
@ -465,21 +471,20 @@ class TwoPartyTradeProtocolTests {
|
|||||||
output("alice's paper") {
|
output("alice's paper") {
|
||||||
CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), owner, amount, TEST_TX_TIME + 7.days)
|
CommercialPaper.State(MEGA_CORP.ref(1, 2, 3), owner, amount, TEST_TX_TIME + 7.days)
|
||||||
}
|
}
|
||||||
arg(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
command(MEGA_CORP_PUBKEY) { CommercialPaper.Commands.Issue() }
|
||||||
if (!withError)
|
if (!withError)
|
||||||
arg(notary.owningKey) { TimestampCommand(TEST_TX_TIME, 30.seconds) }
|
command(notary.owningKey) { TimestampCommand(TEST_TX_TIME, 30.seconds) }
|
||||||
if (attachmentID != null)
|
if (attachmentID != null)
|
||||||
attachment(attachmentID)
|
attachment(attachmentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
val wallet = Wallet(listOf<StateAndRef<Cash.State>>(lookup("alice's paper")))
|
val wallet = Wallet(listOf("alice's paper".outputStateAndRef()))
|
||||||
return Pair(wallet, listOf(ap))
|
return Pair(wallet, listOf(ap))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RecordingTransactionStorage(val delegate: TransactionStorage) : TransactionStorage {
|
class RecordingTransactionStorage(val delegate: TransactionStorage) : TransactionStorage {
|
||||||
|
|
||||||
val records = Collections.synchronizedList(ArrayList<TxRecord>())
|
val records: MutableList<TxRecord> = Collections.synchronizedList(ArrayList<TxRecord>())
|
||||||
|
|
||||||
override fun addTransaction(transaction: SignedTransaction) {
|
override fun addTransaction(transaction: SignedTransaction) {
|
||||||
records.add(TxRecord.Add(transaction))
|
records.add(TxRecord.Add(transaction))
|
||||||
|
@ -2,35 +2,34 @@ package com.r3corda.node.visualiser
|
|||||||
|
|
||||||
import com.r3corda.core.contracts.CommandData
|
import com.r3corda.core.contracts.CommandData
|
||||||
import com.r3corda.core.contracts.ContractState
|
import com.r3corda.core.contracts.ContractState
|
||||||
import com.r3corda.core.contracts.TransactionState
|
|
||||||
import com.r3corda.core.crypto.SecureHash
|
import com.r3corda.core.crypto.SecureHash
|
||||||
import com.r3corda.core.testing.TransactionGroupDSL
|
import com.r3corda.core.testing.*
|
||||||
import org.graphstream.graph.Edge
|
import org.graphstream.graph.Edge
|
||||||
import org.graphstream.graph.Node
|
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: TransactionGroupDSL<in ContractState>) {
|
class GraphVisualiser(val dsl: LedgerDsl<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()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun convert(): SingleGraph {
|
fun convert(): SingleGraph {
|
||||||
val tg = dsl.toTransactionGroup()
|
val tg = dsl.interpreter.toTransactionGroup()
|
||||||
val graph = createGraph("Transaction group", css)
|
val graph = createGraph("Transaction group", css)
|
||||||
|
|
||||||
// Map all the transactions, including the bogus non-verified ones (with no inputs) to graph nodes.
|
// Map all the transactions, including the bogus non-verified ones (with no inputs) to graph nodes.
|
||||||
for ((txIndex, tx) in (tg.transactions + tg.nonVerifiedRoots).withIndex()) {
|
for ((txIndex, tx) in (tg.transactions + tg.nonVerifiedRoots).withIndex()) {
|
||||||
val txNode = graph.addNode<Node>("tx$txIndex")
|
val txNode = graph.addNode<Node>("tx$txIndex")
|
||||||
if (tx !in tg.nonVerifiedRoots)
|
if (tx !in tg.nonVerifiedRoots)
|
||||||
txNode.label = dsl.labelForTransaction(tx).let { it ?: "TX ${tx.id.prefixChars()}" }
|
txNode.label = dsl.interpreter.transactionName(tx.id).let { it ?: "TX[${tx.id.prefixChars()}]" }
|
||||||
txNode.styleClass = "tx"
|
txNode.styleClass = "tx"
|
||||||
|
|
||||||
// Now create a vertex for each output state.
|
// Now create a vertex for each output state.
|
||||||
for (outIndex in tx.outputs.indices) {
|
for (outIndex in tx.outputs.indices) {
|
||||||
val node = graph.addNode<Node>(tx.outRef<ContractState>(outIndex).ref.toString())
|
val node = graph.addNode<Node>(tx.outRef<ContractState>(outIndex).ref.toString())
|
||||||
val state = tx.outputs[outIndex]
|
val state = tx.outputs[outIndex]
|
||||||
node.label = stateToLabel(state)
|
node.label = stateToLabel(state.data)
|
||||||
node.styleClass = stateToCSSClass(state.data) + ",state"
|
node.styleClass = stateToCSSClass(state.data) + ",state"
|
||||||
node.setAttribute("state", state)
|
node.setAttribute("state", state)
|
||||||
val edge = graph.addEdge<Edge>("tx$txIndex-out$outIndex", txNode, node, true)
|
val edge = graph.addEdge<Edge>("tx$txIndex-out$outIndex", txNode, node, true)
|
||||||
@ -56,8 +55,8 @@ class GraphVisualiser(val dsl: TransactionGroupDSL<in ContractState>) {
|
|||||||
return graph
|
return graph
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stateToLabel(state: TransactionState<*>): String {
|
private fun stateToLabel(state: ContractState): String {
|
||||||
return dsl.labelForState(state) ?: stateToTypeName(state.data)
|
return dsl.interpreter.outputToLabel(state) ?: stateToTypeName(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun commandToTypeName(state: CommandData) = state.javaClass.canonicalName.removePrefix("contracts.").replace('$', '.')
|
private fun commandToTypeName(state: CommandData) = state.javaClass.canonicalName.removePrefix("contracts.").replace('$', '.')
|
||||||
|
Reference in New Issue
Block a user