mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
Merged in rnicoll-ricardian (pull request #90)
Add issuance definition for cash contract
This commit is contained in:
commit
3f3ab74f1e
@ -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