diff --git a/contracts/onchain/note.es b/contracts/onchain/note.es index b1331ce..e3263b7 100644 --- a/contracts/onchain/note.es +++ b/contracts/onchain/note.es @@ -16,7 +16,7 @@ // tree contains reserveId as a key, signature as value, // and message under the signature is position in the tree, note value and token id // R5 - current holder of the note (public key given as a group element) - // R6 - current length of the chain (as long int) + // R6 - current length of the spendings chain (as long int) // // tokens: // #0 - token which amount is equal to note value at the moment of issueance, in unit of account (mg of gold). diff --git a/contracts/onchain/registry.es b/contracts/onchain/registry.es new file mode 100644 index 0000000..5d433bd --- /dev/null +++ b/contracts/onchain/registry.es @@ -0,0 +1,9 @@ +{ + // + // tokens: + // #0 - note registration token + + val selfOut = OUTPUTS + + +} \ No newline at end of file diff --git a/contracts/onchain/reserve.es b/contracts/onchain/reserve.es index 65e88e6..3b9ec83 100644 --- a/contracts/onchain/reserve.es +++ b/contracts/onchain/reserve.es @@ -4,15 +4,15 @@ // Data: // - token #0 - identifying singleton token // - R4 - signing key (as a group element) - // - R5 - refund init height (None if not set) - // - R6 - amount to refund (None if not set) + // - R5 - trees of all the note tokens issued TODO: check preservation in actions // // Actions: // - redeem note (#0) // - top up (#1) - // - init refund (#2) - // - cancel refund (#3) - // - complete refund (#4) + // - mint note (#2) + + // we allow multiple reserves to be used in a transaction, for that, single context extension variable #0 + // is encoding both action to take and output index in following way: val v = getVar[Byte](0).get val action = v / 10 @@ -20,6 +20,8 @@ val ownerKey = SELF.R4[GroupElement].get // reserve owner's key, used in notes and unlock/lock/refund actions val selfOut = OUTPUTS(index) + + // common checks for all the paths (not incl. ERG value check) val selfPreserved = selfOut.propositionBytes == SELF.propositionBytes && selfOut.tokens == SELF.tokens && @@ -32,6 +34,15 @@ // #1 - receipt // #2 - buyback + // Context var used: + // #1 + // #2 + // #3 + // #4 + // #5 + // #6 - note creation height + // #7 - note token proof + val g: GroupElement = groupGenerator // if set, re-redemption against receipt data is done, otherwise, a note is redeemed @@ -110,7 +121,7 @@ receiptOut.tokens(0) == noteInput.tokens(0) && receiptOut.R4[AvlTree].get == history && receiptOut.R5[Long].get == position && - receiptOut.R6[Int].get >= HEIGHT - 20 && // 20 blocks for inclusion + receiptOut.R6[Int].get >= HEIGHT - 10 && // 10 blocks for inclusion receiptOut.R6[Int].get <= HEIGHT && receiptOut.R7[GroupElement].get == ownerKey @@ -121,32 +132,24 @@ true } - sigmaProp(selfPreserved && properOracle && redeemCorrect && properSignature && properReceipt && positionCorrect) + val noteRegistryInputIdx = getVar[Int](6).get + val noteRegistryInput = CONTEXT.dataInputs(noteRegistryInputIdx) + val registrationTokenCorrect = noteRegistryInput.tokens(0)._1 == fromBase16("") // todo: set value + val registrationBoxCorrect = noteRegistryInput.R4[Coll[Byte]].get == noteTokenId + val creationHeight = noteRegistryInput.R5[Int].get + + val redemptionTimeCorrect = (HEIGHT - creationHeight > 7200) && // 10 days + registrationTokenCorrect && + registrationBoxCorrect + + sigmaProp(selfPreserved && properOracle && redeemCorrect && properSignature && properReceipt && + positionCorrect && redemptionTimeCorrect) } else if (action == 1) { // top up + // anyone can add, so we have 1 ERG minimum to add to avoid spam sigmaProp(selfPreserved && (selfOut.value - SELF.value >= 1000000000)) // at least 1 ERG added } else { - // todo: write tests for refund paths, document them - if (action == 2) { - // init refund - val correctHeight = selfOut.R5[Int].get >= HEIGHT - 5 - val correctValue = selfOut.value >= SELF.value - sigmaProp(selfPreserved && correctHeight && correctValue) && proveDlog(ownerKey) - } else if (action == 3) { - // cancel refund - val correctHeight = !(selfOut.R5[Int].isDefined) - val correctValue = selfOut.value >= SELF.value - sigmaProp(selfPreserved && correctHeight && correctValue) && proveDlog(ownerKey) - } else if (action == 4) { - // complete refund - val refundNotificationPeriod = 14400 // 20 days - val correctHeight = (SELF.R5[Int].get + refundNotificationPeriod) <= HEIGHT - val refundLimit = SELF.R6[Long].get - val correctValue = SELF.value - selfOut.value <= refundLimit - sigmaProp(selfPreserved && correctHeight && correctValue) && proveDlog(ownerKey) // todo: check is it ok to check no other conditions - } else { - sigmaProp(false) - } + sigmaProp(false) } } \ No newline at end of file diff --git a/docs/ramics/.gitignore b/docs/conf/.gitignore similarity index 100% rename from docs/ramics/.gitignore rename to docs/conf/.gitignore diff --git a/docs/ramics/IEEEtran.cls b/docs/conf/IEEEtran.cls similarity index 100% rename from docs/ramics/IEEEtran.cls rename to docs/conf/IEEEtran.cls diff --git a/docs/conf/compile.sh b/docs/conf/compile.sh new file mode 100755 index 0000000..f2ce08c --- /dev/null +++ b/docs/conf/compile.sh @@ -0,0 +1,14 @@ +rm conf.aux +rm conf.out +rm conf.log +rm conf.bbl +rm conf.blg +pdflatex conf +bibtex conf +pdflatex conf +pdflatex conf +rm conf.aux +rm conf.out +rm conf.log +rm conf.bbl +rm conf.blg diff --git a/docs/ramics/ramics.tex b/docs/conf/conf.tex similarity index 98% rename from docs/ramics/ramics.tex rename to docs/conf/conf.tex index 05c27a5..1938c5f 100644 --- a/docs/ramics/ramics.tex +++ b/docs/conf/conf.tex @@ -63,7 +63,7 @@ \title{Money Creation With Elastic Supply Via Trust And Blockchain Assets In Global Digital Peer-to-Peer Environment} -\author{kushti \\ \href{mailto:kushti@protonmail.ch}{kushti@protonmail.ch} \and scalahub} +% \author{kushti \\ \href{mailto:kushti@protonmail.ch}{kushti@protonmail.ch} \and scalahub} \maketitle @@ -73,7 +73,7 @@ protocol allows for elastic money creation in peer-to-peer environment. Acceptance of notes created or signed by other peers made an individual choice. Similarly to spender-signed currencies, every spender is signing and backing a note. Every signature is associated with a reserve, which can be made of any blockchain token, or be empty. A note may -be redeemed against any reserve of its signers, and then re-redeemed against a reserve associated with earlier signature. +be redeemed against any reserve of its signers, and then re-redeemed against a reserve associated with an earlier signature. We have implemented our proposal in software in form of a payment server, supporting flexible rules for notes acceptance, based on blacklist, whitelist, collateralization ratio. We have shown how to implement a local exchange trading system (LETS) @@ -82,7 +82,7 @@ \section{Introduction} -Currently, most of monetary value is created by private banks~(often, offshore banks as in so-called \"eurodollar\" +Currently, most of monetary value is created by private banks~(often, offshore banks as in so-called eurodollar system~\cite{machlup1970euro}), following central banks requirements. As an alternative, starting with Bitcoin~\cite{nakamoto2008peer} launch in 2009, a lot of cryptocurrencies and applications on top of public blockchains are experimenting with algorithmic money issuance. As another option, we also have alternative (usually, local), diff --git a/docs/ramics/sources.bib b/docs/conf/sources.bib similarity index 100% rename from docs/ramics/sources.bib rename to docs/conf/sources.bib diff --git a/docs/ramics/compile.sh b/docs/ramics/compile.sh deleted file mode 100755 index f9b6bc5..0000000 --- a/docs/ramics/compile.sh +++ /dev/null @@ -1,14 +0,0 @@ -rm ramics.aux -rm ramics.out -rm ramics.log -rm ramics.bbl -rm ramics.blg -pdflatex ramics -bibtex ramics -pdflatex ramics -pdflatex ramics -rm ramics.aux -rm ramics.out -rm ramics.log -rm ramics.bbl -rm ramics.blg diff --git a/src/test/scala/chaincash/ChainCashSpec.scala b/src/test/scala/chaincash/ChainCashSpec.scala index a01fee7..137bda9 100644 --- a/src/test/scala/chaincash/ChainCashSpec.scala +++ b/src/test/scala/chaincash/ChainCashSpec.scala @@ -12,6 +12,7 @@ import org.scalatest.{Matchers, PropSpec} import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks import scorex.crypto.hash.Blake2b256 import scorex.util.encode.{Base16, Base64} +import scorex.util.idToBytes import sigmastate.AvlTreeFlags import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.eval._ @@ -226,16 +227,24 @@ class ChainCashSpec extends PropSpec with Matchers with ScalaCheckDrivenProperty val msg: Array[Byte] = positionBytes ++ Longs.toByteArray(noteValue) ++ Base16.decode(noteTokenId).get val sig = SigUtils.sign(msg, holderSecret) - val plasmaMap = new PlasmaMap[Array[Byte], Array[Byte]](AvlTreeFlags.InsertOnly, chainCashPlasmaParameters) + val sigMap = new PlasmaMap[Array[Byte], Array[Byte]](AvlTreeFlags.InsertOnly, chainCashPlasmaParameters) val sigBytes = GroupElementSerializer.toBytes(sig._1) ++ sig._2.toByteArray val keyBytes = positionBytes ++ reserveNFTBytes - val insertRes = plasmaMap.insert(keyBytes -> sigBytes) + val insertRes = sigMap.insert(keyBytes -> sigBytes) val _ = insertRes.proof - val historyTree = plasmaMap.ergoValue.getValue - - val lookupRes = plasmaMap.lookUp(keyBytes) + val historyTree = sigMap.ergoValue.getValue + val lookupRes = sigMap.lookUp(keyBytes) val lookupProof = lookupRes.proof + val noteCreationHeight = ctx.getHeight - 7201 + val noteTokensMap = new PlasmaMap[Array[Byte], Array[Byte]](AvlTreeFlags.InsertOnly, PlasmaParameters(32, None)) + val noteIdBytes = Base16.decode(noteTokenId).get + val noteHeightBytes = Longs.toByteArray(noteCreationHeight) + noteTokensMap.insert(noteIdBytes -> noteHeightBytes) + val noteTokensTree = noteTokensMap.ergoValue.getValue + val noteHeightLookupRes = noteTokensMap.lookUp(noteIdBytes) + val noteHeightLookupProof = noteHeightLookupRes.proof + val noteInput = ctx .newTxBuilder() @@ -256,7 +265,7 @@ class ChainCashSpec extends PropSpec with Matchers with ScalaCheckDrivenProperty .outBoxBuilder .value(minValue) .tokens(new ErgoToken(reserveNFTBytes, 1)) - .registers(ErgoValue.of(holderPk)) + .registers(ErgoValue.of(holderPk), ErgoValue.of(noteTokensTree)) .contract(ctx.compileContract(ConstantsBuilder.empty(), Constants.reserveContract)) .build() .convertToInputWith(fakeTxId2, fakeIndex) @@ -265,7 +274,9 @@ class ChainCashSpec extends PropSpec with Matchers with ScalaCheckDrivenProperty new ContextVar(1, ErgoValue.of(lookupProof.bytes)), new ContextVar(2, ErgoValue.of(Longs.toByteArray(noteValue))), new ContextVar(3, ErgoValue.of(position)), - new ContextVar(4, ErgoValue.of(false)) + new ContextVar(4, ErgoValue.of(false)), + new ContextVar(6, ErgoValue.of(noteCreationHeight.toLong)), + new ContextVar(7, ErgoValue.of(noteHeightLookupProof.bytes)) ) val buyBackInput = @@ -842,65 +853,4 @@ class ChainCashSpec extends PropSpec with Matchers with ScalaCheckDrivenProperty } } - property("refund - init") { - createMockedErgoClient(MockData(Nil, Nil)).execute { implicit ctx: BlockchainContext => - val reserveInput = - ctx - .newTxBuilder() - .outBoxBuilder - .value(minValue) - .tokens(new ErgoToken(reserveNFTBytes, 1)) - .registers(ErgoValue.of(holderPk)) - .contract(ctx.compileContract(ConstantsBuilder.empty(), Constants.reserveContract)) - .build() - .convertToInputWith(fakeTxId2, fakeIndex) - .withContextVars( - new ContextVar(0, ErgoValue.of(20: Byte)) - ) - - val fundingBox = - ctx - .newTxBuilder() - .outBoxBuilder - .value(minValue) - .contract(ctx.compileContract(ConstantsBuilder.empty(), fakeScript)) - .build() - .convertToInputWith(fakeTxId1, fakeIndex) - - val reserveOutput = createOut( - Constants.reserveContract, - minValue, - registers = Array(ErgoValue.of(holderPk), ErgoValue.of(ctx.getHeight - 2)), - tokens = Array(new ErgoToken(reserveNFT, 1)) - ) - - val inputs = Array[InputBox](fundingBox, reserveInput) - val dataInputs = Array[InputBox]() - val outputs = Array[OutBoxImpl](reserveOutput) - - createTx( - inputs, - dataInputs, - outputs, - fee = None , - changeAddress, - Array[String](holderSecret.toString()), - false - ) - } - } - - property("refund - cancel") { - // todo: - } - - - property("refund - done") { - // todo: - } - - property("top-up") { - // todo: - } - }