Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/onchain/note.es
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
9 changes: 9 additions & 0 deletions contracts/onchain/registry.es
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
//
// tokens:
// #0 - note registration token

val selfOut = OUTPUTS


}
59 changes: 31 additions & 28 deletions contracts/onchain/reserve.es
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@
// 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
val index = v % 10

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 &&
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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)
}

}
File renamed without changes.
File renamed without changes.
14 changes: 14 additions & 0 deletions docs/conf/compile.sh
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions docs/ramics/ramics.tex → docs/conf/conf.tex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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),
Expand Down
File renamed without changes.
14 changes: 0 additions & 14 deletions docs/ramics/compile.sh

This file was deleted.

86 changes: 18 additions & 68 deletions src/test/scala/chaincash/ChainCashSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand All @@ -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 =
Expand Down Expand Up @@ -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:
}

}