Contracts: Rename ComedyPaper to CommercialPaper and rewrite the comment at the top to flag its not-realistic status.

This commit is contained in:
Mike Hearn 2015-11-16 12:42:16 +01:00
parent 379d531cf0
commit d1a605de6f
4 changed files with 25 additions and 23 deletions

View File

@ -5,10 +5,14 @@ import java.security.PublicKey
import java.time.Instant
/**
* "Comedy paper" is basically like commercial paper, but modelled by a non-expert and without any attention paid
* to the issuance aspect. It has a weird name to emphasise that it's not a prototype of real CP, as that's currently
* waiting for Jo/Ayoub to deliver full CP use case models. This file may be renamed later if it grows up and
* becomes real CP.
* This is an ultra-trivial implementation of commercial paper, which is essentially a simpler version of a corporate
* bond. It can be seen as a company-specific currency. A company issues CP with a particular face value, like $100
* but sells it for less, say $90. The paper can be redeemed for cash at a given date in the future. Thus this example
* would have a 10% interest rate with a single repayment. Commercial paper is often rolled out (redeemed and the
* money used to immediately rebuy).
*
* This contract is not intended to realistically model CP. It is here only to act as a next step up above cash in
* the prototyping phase. It is thus very incomplete.
*
* Open issues:
* - In this model, you cannot merge or split CP. Can you do this normally? We could model CP as a specialised form
@ -17,13 +21,11 @@ import java.time.Instant
* issue: need to find a cleaner way to allow this. Does the single-execution-per-transaction model make sense?
*/
// TODO: This is very incomplete! Do not attempt to find mistakes in it just yet.
val CP_PROGRAM_ID = SecureHash.sha256("comedy-paper")
val CP_PROGRAM_ID = SecureHash.sha256("replace-me-later-with-bytecode-hash")
// TODO: Generalise the notion of an owned instrument into a superclass/supercontract. Consider composition vs inheritance.
object ComedyPaper : Contract {
override val legalContractReference: String = "https://www.gotlines.com/jokes/1"
object CommercialPaper : Contract {
override val legalContractReference: String = "https://en.wikipedia.org/wiki/Commercial_paper"
data class State(
val issuance: InstitutionReference,
@ -45,10 +47,10 @@ object ComedyPaper : Contract {
with(tx) {
// There are two possible things that can be done with CP. The first is trading it. The second is redeeming it
// for cash on or after the maturity date.
val command = args.requireSingleCommand<ComedyPaper.Commands>()
val command = args.requireSingleCommand<CommercialPaper.Commands>()
// For now do not allow multiple pieces of CP to trade in a single transaction. Study this more!
val input = inStates.filterIsInstance<ComedyPaper.State>().single()
val input = inStates.filterIsInstance<CommercialPaper.State>().single()
requireThat {
"the transaction is signed by the owner of the CP" by (command.signers.contains(input.owner))
@ -56,7 +58,7 @@ object ComedyPaper : Contract {
when (command.value) {
is Commands.Move -> requireThat {
val output = outStates.filterIsInstance<ComedyPaper.State>().single()
val output = outStates.filterIsInstance<CommercialPaper.State>().single()
"the output state is the same as the input state except for owner" by (input.withoutOwner() == output.withoutOwner())
}
@ -65,7 +67,7 @@ object ComedyPaper : Contract {
// Do we need to check the signature of the issuer here too?
"the paper must have matured" by (input.maturityDate < time)
"the received amount equals the face value" by (received == input.faceValue)
"the paper must be destroyed" by outStates.filterIsInstance<ComedyPaper.State>().none()
"the paper must be destroyed" by outStates.filterIsInstance<CommercialPaper.State>().none()
}
}
}

View File

@ -35,7 +35,7 @@ val TEST_TX_TIME = Instant.parse("2015-04-17T12:00:00.00Z")
// a sandbox. For now we just instantiate right at the start of the program.
val TEST_PROGRAM_MAP: Map<SecureHash, Contract> = mapOf(
CASH_PROGRAM_ID to Cash,
CP_PROGRAM_ID to ComedyPaper,
CP_PROGRAM_ID to CommercialPaper,
DUMMY_PROGRAM_ID to DummyContract
)

View File

@ -7,7 +7,7 @@ import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output
import com.esotericsoftware.kryo.serializers.JavaSerializer
import contracts.Cash
import contracts.ComedyPaper
import contracts.CommercialPaper
import core.*
import java.io.ByteArrayOutputStream
import java.lang.reflect.InvocationTargetException
@ -223,9 +223,9 @@ fun createKryo(): Kryo {
registerDataClass<Cash.State>()
register(Cash.Commands.Move.javaClass)
registerDataClass<Cash.Commands.Exit>()
registerDataClass<ComedyPaper.State>()
register(ComedyPaper.Commands.Move.javaClass)
register(ComedyPaper.Commands.Redeem.javaClass)
registerDataClass<CommercialPaper.State>()
register(CommercialPaper.Commands.Move.javaClass)
register(CommercialPaper.Commands.Redeem.javaClass)
// And for unit testing ...
registerDataClass<DummyPublicKey>()

View File

@ -5,8 +5,8 @@ import org.junit.Test
// TODO: Finish this off.
class ComedyPaperTests {
val PAPER_1 = ComedyPaper.State(
class CommercialPaperTests {
val PAPER_1 = CommercialPaper.State(
issuance = InstitutionReference(MEGA_CORP, OpaqueBytes.of(123)),
owner = DUMMY_PUBKEY_1,
faceValue = 1000.DOLLARS,
@ -30,15 +30,15 @@ class ComedyPaperTests {
this.rejects()
transaction {
arg(DUMMY_PUBKEY_2) { ComedyPaper.Commands.Move }
arg(DUMMY_PUBKEY_2) { CommercialPaper.Commands.Move }
this `fails requirement` "is signed by the owner"
}
arg(DUMMY_PUBKEY_1) { ComedyPaper.Commands.Move }
arg(DUMMY_PUBKEY_1) { CommercialPaper.Commands.Move }
arg(DUMMY_PUBKEY_1) { Cash.Commands.Move }
this.accepts()
}.chain("a") {
arg(DUMMY_PUBKEY_2, MINI_CORP_KEY) { ComedyPaper.Commands.Redeem }
arg(DUMMY_PUBKEY_2, MINI_CORP_KEY) { CommercialPaper.Commands.Redeem }
// No cash output, can't redeem like that!
this.rejects("no cash being redeemed")