mirror of
https://github.com/corda/corda.git
synced 2024-12-26 08:01:09 +00:00
Add issuance definition for cash contract
Add issuance definition for cash contract, as well as common interfaces to support later extensions. The issuance definition encapsulates the core values for state objects when issued, and essentially acts as the Ricardian contract for Corda states.
This commit is contained in:
parent
3ee601360e
commit
25e2c4bc4d
contracts/src/main/kotlin/contracts
core/src/main/kotlin/core
src/test/kotlin/contracts
@ -1,5 +1,7 @@
|
||||
package contracts
|
||||
|
||||
import contracts.cash.CashIssuanceDefinition
|
||||
import contracts.cash.CommonCashState
|
||||
import core.*
|
||||
import core.crypto.SecureHash
|
||||
import core.crypto.toStringShort
|
||||
@ -45,17 +47,27 @@ class Cash : Contract {
|
||||
*/
|
||||
override val legalContractReference: SecureHash = SecureHash.sha256("https://www.big-book-of-banking-law.gov/cash-claims.html")
|
||||
|
||||
data class IssuanceDefinition(
|
||||
/** Where the underlying currency backing this ledger entry can be found (propagated) */
|
||||
override val deposit: PartyAndReference,
|
||||
|
||||
override val currency: Currency
|
||||
) : CashIssuanceDefinition
|
||||
|
||||
/** A state representing a cash claim against some party */
|
||||
data class State(
|
||||
/** Where the underlying currency backing this ledger entry can be found (propagated) */
|
||||
val deposit: PartyAndReference,
|
||||
override val deposit: PartyAndReference,
|
||||
|
||||
val amount: Amount,
|
||||
override val amount: Amount,
|
||||
|
||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||
override val owner: PublicKey
|
||||
) : OwnableState {
|
||||
) : CommonCashState<Cash.IssuanceDefinition> {
|
||||
override val issuanceDef: Cash.IssuanceDefinition
|
||||
get() = Cash.IssuanceDefinition(deposit, amount.currency)
|
||||
override val contract = CASH_PROGRAM_ID
|
||||
|
||||
override fun toString() = "${Emoji.bagOfCash}Cash($amount at $deposit owned by ${owner.toStringShort()})"
|
||||
|
||||
override fun withNewOwner(newOwner: PublicKey) = Pair(Commands.Move(), copy(owner = newOwner))
|
||||
@ -82,11 +94,12 @@ class Cash : Contract {
|
||||
override fun verify(tx: TransactionForVerification) {
|
||||
// Each group is a set of input/output states with distinct (deposit, currency) attributes. These types
|
||||
// of cash are not fungible and must be kept separated for bookkeeping purposes.
|
||||
val groups = tx.groupStates() { it: Cash.State -> Pair(it.deposit, it.amount.currency) }
|
||||
val groups = tx.groupStates() { it: Cash.State -> it.issuanceDef }
|
||||
|
||||
for ((inputs, outputs, key) in groups) {
|
||||
// Either inputs or outputs could be empty.
|
||||
val (deposit, currency) = key
|
||||
val deposit = key.deposit
|
||||
val currency = key.currency
|
||||
val issuer = deposit.party
|
||||
|
||||
requireThat {
|
||||
@ -144,6 +157,12 @@ class Cash : Contract {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts together an issuance transaction from the given template, that starts out being owned by the given pubkey.
|
||||
*/
|
||||
fun generateIssue(tx: TransactionBuilder, issuanceDef: CashIssuanceDefinition, pennies: Long, owner: PublicKey)
|
||||
= generateIssue(tx, Amount(pennies, issuanceDef.currency), issuanceDef.deposit, owner)
|
||||
|
||||
/**
|
||||
* Puts together an issuance transaction for the specified amount that starts out being owned by the given pubkey.
|
||||
*/
|
||||
|
@ -0,0 +1,15 @@
|
||||
package contracts.cash
|
||||
|
||||
import core.IssuanceDefinition
|
||||
import core.PartyAndReference
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Subset of cash-like contract state, containing the issuance definition. If these definitions match for two
|
||||
* contracts' states, those states can be aggregated.
|
||||
*/
|
||||
interface CashIssuanceDefinition : IssuanceDefinition {
|
||||
/** Where the underlying currency backing this ledger entry can be found (propagated) */
|
||||
val deposit: PartyAndReference
|
||||
val currency: Currency
|
||||
}
|
15
contracts/src/main/kotlin/contracts/cash/CommonCashState.kt
Normal file
15
contracts/src/main/kotlin/contracts/cash/CommonCashState.kt
Normal file
@ -0,0 +1,15 @@
|
||||
package contracts.cash
|
||||
|
||||
import core.Amount
|
||||
import core.OwnableState
|
||||
import core.PartyAndReference
|
||||
|
||||
/**
|
||||
* Common elements of cash contract states.
|
||||
*/
|
||||
interface CommonCashState<I : CashIssuanceDefinition> : OwnableState {
|
||||
val issuanceDef: I
|
||||
/** Where the underlying currency backing this ledger entry can be found (propagated) */
|
||||
val deposit: PartyAndReference
|
||||
val amount: Amount
|
||||
}
|
@ -29,6 +29,15 @@ interface ContractState {
|
||||
val contract: Contract
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker interface for data classes that represent the issuance state for a contract. These are intended as templates
|
||||
* from which the state object is initialised.
|
||||
*/
|
||||
interface IssuanceDefinition
|
||||
|
||||
/**
|
||||
* A contract state that can have a single owner.
|
||||
*/
|
||||
interface OwnableState : ContractState {
|
||||
/** There must be a MoveCommand signed by this key to claim the amount */
|
||||
val owner: PublicKey
|
||||
@ -189,4 +198,4 @@ interface Attachment : NamedByHash {
|
||||
}
|
||||
throw FileNotFoundException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,7 @@
|
||||
/*
|
||||
* Copyright 2015 Distributed Ledger Group LLC. Distributed as Licensed Company IP to DLG Group Members
|
||||
* pursuant to the August 7, 2015 Advisory Services Agreement and subject to the Company IP License terms
|
||||
* set forth therein.
|
||||
*
|
||||
* All other rights reserved.
|
||||
*/
|
||||
|
||||
import contracts.Cash
|
||||
import contracts.DummyContract
|
||||
import contracts.InsufficientBalanceException
|
||||
import contracts.cash.CashIssuanceDefinition
|
||||
import core.*
|
||||
import core.crypto.SecureHash
|
||||
import core.serialization.OpaqueBytes
|
||||
@ -18,6 +11,7 @@ import java.security.PublicKey
|
||||
import java.util.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class CashTests {
|
||||
@ -110,6 +104,13 @@ class CashTests {
|
||||
assertTrue(ptx.commands()[0].value is Cash.Commands.Issue)
|
||||
assertEquals(MINI_CORP_PUBKEY, ptx.commands()[0].signers[0])
|
||||
|
||||
// Test issuance from the issuance definition
|
||||
val issuanceDef = Cash.IssuanceDefinition(MINI_CORP.ref(12, 34), USD)
|
||||
val templatePtx = TransactionBuilder()
|
||||
Cash().generateIssue(templatePtx, issuanceDef, 100.DOLLARS.pennies, owner = DUMMY_PUBKEY_1)
|
||||
assertTrue(templatePtx.inputStates().isEmpty())
|
||||
assertEquals(ptx.outputStates()[0], templatePtx.outputStates()[0])
|
||||
|
||||
// We can consume $1000 in a transaction and output $2000 as long as it's signed by an issuer.
|
||||
transaction {
|
||||
input { inState }
|
||||
@ -423,4 +424,32 @@ class CashTests {
|
||||
makeSpend(81.SWISS_FRANCS, THEIR_PUBKEY_1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that aggregation of states is correctly modelled.
|
||||
*/
|
||||
@Test
|
||||
fun aggregation() {
|
||||
val fiveThousandDollarsFromMega = Cash.State(MEGA_CORP.ref(2), 5000.DOLLARS, MEGA_CORP_PUBKEY)
|
||||
val twoThousandDollarsFromMega = Cash.State(MEGA_CORP.ref(2), 2000.DOLLARS, MINI_CORP_PUBKEY)
|
||||
val oneThousandDollarsFromMini = Cash.State(MINI_CORP.ref(3), 1000.DOLLARS, MEGA_CORP_PUBKEY)
|
||||
|
||||
// Obviously it must be possible to aggregate states with themselves
|
||||
assertEquals(fiveThousandDollarsFromMega.issuanceDef, fiveThousandDollarsFromMega.issuanceDef)
|
||||
|
||||
// Owner is not considered when calculating whether it is possible to aggregate states
|
||||
assertEquals(fiveThousandDollarsFromMega.issuanceDef, twoThousandDollarsFromMega.issuanceDef)
|
||||
|
||||
// States cannot be aggregated if the deposit differs
|
||||
assertNotEquals(fiveThousandDollarsFromMega.issuanceDef, oneThousandDollarsFromMini.issuanceDef)
|
||||
assertNotEquals(twoThousandDollarsFromMega.issuanceDef, oneThousandDollarsFromMini.issuanceDef)
|
||||
|
||||
// States cannot be aggregated if the currency differs
|
||||
assertNotEquals(oneThousandDollarsFromMini.issuanceDef,
|
||||
Cash.State(MINI_CORP.ref(3), 1000.POUNDS, MEGA_CORP_PUBKEY).issuanceDef)
|
||||
|
||||
// States cannot be aggregated if the reference differs
|
||||
assertNotEquals(fiveThousandDollarsFromMega.issuanceDef, fiveThousandDollarsFromMega.copy(deposit = MEGA_CORP.ref(1)).issuanceDef)
|
||||
assertNotEquals(fiveThousandDollarsFromMega.copy(deposit = MEGA_CORP.ref(1)).issuanceDef, fiveThousandDollarsFromMega.issuanceDef)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user