From d1a605de6fe5ac90597b237920c951c6a644ab8f Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Mon, 16 Nov 2015 12:42:16 +0100 Subject: [PATCH] Contracts: Rename ComedyPaper to CommercialPaper and rewrite the comment at the top to flag its not-realistic status. --- .../{ComedyPaper.kt => CommercialPaper.kt} | 28 ++++++++++--------- src/core/TestUtils.kt | 2 +- src/core/serialization/Kryo.kt | 8 +++--- ...yPaperTests.kt => CommercialPaperTests.kt} | 10 +++---- 4 files changed, 25 insertions(+), 23 deletions(-) rename src/contracts/{ComedyPaper.kt => CommercialPaper.kt} (66%) rename tests/contracts/{ComedyPaperTests.kt => CommercialPaperTests.kt} (84%) diff --git a/src/contracts/ComedyPaper.kt b/src/contracts/CommercialPaper.kt similarity index 66% rename from src/contracts/ComedyPaper.kt rename to src/contracts/CommercialPaper.kt index 21a5871da4..32d94a83c2 100644 --- a/src/contracts/ComedyPaper.kt +++ b/src/contracts/CommercialPaper.kt @@ -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() + val command = args.requireSingleCommand() // For now do not allow multiple pieces of CP to trade in a single transaction. Study this more! - val input = inStates.filterIsInstance().single() + val input = inStates.filterIsInstance().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().single() + val output = outStates.filterIsInstance().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().none() + "the paper must be destroyed" by outStates.filterIsInstance().none() } } } diff --git a/src/core/TestUtils.kt b/src/core/TestUtils.kt index 73c935d5f9..71a6585112 100644 --- a/src/core/TestUtils.kt +++ b/src/core/TestUtils.kt @@ -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 = mapOf( CASH_PROGRAM_ID to Cash, - CP_PROGRAM_ID to ComedyPaper, + CP_PROGRAM_ID to CommercialPaper, DUMMY_PROGRAM_ID to DummyContract ) diff --git a/src/core/serialization/Kryo.kt b/src/core/serialization/Kryo.kt index 9fe35b84a4..4d56bb4da5 100644 --- a/src/core/serialization/Kryo.kt +++ b/src/core/serialization/Kryo.kt @@ -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() register(Cash.Commands.Move.javaClass) registerDataClass() - registerDataClass() - register(ComedyPaper.Commands.Move.javaClass) - register(ComedyPaper.Commands.Redeem.javaClass) + registerDataClass() + register(CommercialPaper.Commands.Move.javaClass) + register(CommercialPaper.Commands.Redeem.javaClass) // And for unit testing ... registerDataClass() diff --git a/tests/contracts/ComedyPaperTests.kt b/tests/contracts/CommercialPaperTests.kt similarity index 84% rename from tests/contracts/ComedyPaperTests.kt rename to tests/contracts/CommercialPaperTests.kt index 491b0959a1..0da38f22dc 100644 --- a/tests/contracts/ComedyPaperTests.kt +++ b/tests/contracts/CommercialPaperTests.kt @@ -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")