Skip to content

Commit

Permalink
Merge branch 'contract-review' into 'dev'
Browse files Browse the repository at this point in the history
Fix RWT check on creating and extending permits

Closes #30

See merge request ergo/rosen-bridge/contract!64
  • Loading branch information
vorujack committed Oct 16, 2023
2 parents b7a19af + 3996784 commit 11c5973
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 6 deletions.
8 changes: 8 additions & 0 deletions src/main/scala/rosen/bridge/scripts/Permit.es
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
Coll(
OUTPUTS(1).tokens(0)._1 == SELF.tokens(0)._1,
OUTPUTS(1).propositionBytes == SELF.propositionBytes,
SELF.R4[Coll[Coll[Byte]]].get == OUTPUTS(1).R4[Coll[Coll[Byte]]].get
)
)
}else{
Expand All @@ -35,9 +36,16 @@
}else{
// Event Commitment Creation
// [Permit(s), WID] => [Permit, Commitment, WID]
val totalPermits = INPUTS.filter{(box:Box)
=> box.tokens(0)._1 == SELF.tokens(0)._1
}
.map{(box:Box) => box.tokens(0)._2}
.fold(0L, { (a: Long, b: Long) => a + b })
sigmaProp(
allOf(
Coll(
OUTPUTS(0).tokens(0)._1 == SELF.tokens(0)._1,
OUTPUTS(1).tokens(0)._2 == totalPermits - OUTPUTS(0).tokens(0)._2,
OUTPUTS(1).tokens(0)._1 == SELF.tokens(0)._1,
blake2b256(OUTPUTS(1).propositionBytes) == commitmentScriptHash,
OUTPUTS(1).R5[Coll[Coll[Byte]]].isDefined,
Expand Down
13 changes: 7 additions & 6 deletions src/main/scala/rosen/bridge/scripts/RwtRepo.es
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
repoOut.tokens(0)._2 == repo.tokens(0)._2,
repoOut.tokens(1)._1 == repo.tokens(1)._1,
repoOut.tokens(2)._1 == repo.tokens(2)._1,
repoOut.R4[Coll[Coll[Byte]]].get.size == repoOut.R5[Coll[Long]].get.size,
)
)
if(repo.tokens(1)._2 > repoOut.tokens(1)._2){
Expand All @@ -42,6 +43,7 @@
repoReplication,
RWTOut == repoOut.tokens(2)._2 - repo.tokens(2)._2,
permit.tokens(0)._2 == RWTOut,
permit.tokens(0)._1 == SELF.tokens(1)._1,
blake2b256(permit.propositionBytes) == permitScriptHash,
)
)
Expand All @@ -53,10 +55,9 @@
allOf(
Coll(
permitCreation,
repoOut.R4[Coll[Coll[Byte]]].get.size == widListSize + 1,
widOutListSize == widListSize + 1,
repoOut.R4[Coll[Coll[Byte]]].get.slice(0, widOutListSize - 1) == repo.R4[Coll[Coll[Byte]]].get,
repoOut.R4[Coll[Coll[Byte]]].get(widOutListSize - 1) == repo.id,
repoOut.R5[Coll[Long]].get.size == widListSize + 1,
repoOut.R5[Coll[Long]].get.slice(0, widOutListSize - 1) == repo.R5[Coll[Long]].get,
repoOut.R5[Coll[Long]].get(widOutListSize - 1) == RWTOut,
permit.R4[Coll[Coll[Byte]]].get == Coll(repo.id),
Expand Down Expand Up @@ -103,7 +104,6 @@
val permit = INPUTS(1)
val RWTIn = repoOut.tokens(1)._2 - repo.tokens(1)._2
val WIDIndex = repoOut.R7[Int].get
val watcherCount = repo.R5[Coll[Long]].get.size
val WIDCheckInRepo = if(repo.R5[Coll[Long]].get(WIDIndex) > RWTIn) {
// Returning some RWTs
// [repo, Permit, WIDToken] => [repo, Permit(Optional), WIDToken(+userChange)]
Expand All @@ -113,7 +113,7 @@
repo.R5[Coll[Long]].get(WIDIndex) == repoOut.R5[Coll[Long]].get(WIDIndex) + RWTIn,
repo.R4[Coll[Coll[Byte]]].get == repoOut.R4[Coll[Coll[Byte]]].get,
repo.R5[Coll[Long]].get.slice(0, WIDIndex) == repoOut.R5[Coll[Long]].get.slice(0, WIDIndex),
repo.R5[Coll[Long]].get.slice(WIDIndex + 1, watcherCount) == repoOut.R5[Coll[Long]].get.slice(WIDIndex + 1, watcherCount)
repo.R5[Coll[Long]].get.slice(WIDIndex + 1, widListSize) == repoOut.R5[Coll[Long]].get.slice(WIDIndex + 1, widListSize)
)
)
}else{
Expand All @@ -122,11 +122,12 @@
val watcherCollateral = INPUTS(3);
allOf(
Coll(
widOutListSize == widListSize - 1,
repo.R5[Coll[Long]].get(WIDIndex) == RWTIn,
repo.R4[Coll[Coll[Byte]]].get.slice(0, WIDIndex) == repoOut.R4[Coll[Coll[Byte]]].get.slice(0, WIDIndex),
repo.R4[Coll[Coll[Byte]]].get.slice(WIDIndex + 1, watcherCount) == repoOut.R4[Coll[Coll[Byte]]].get.slice(WIDIndex, watcherCount - 1),
repo.R4[Coll[Coll[Byte]]].get.slice(WIDIndex + 1, widListSize) == repoOut.R4[Coll[Coll[Byte]]].get.slice(WIDIndex, widOutListSize),
repo.R5[Coll[Long]].get.slice(0, WIDIndex) == repoOut.R5[Coll[Long]].get.slice(0, WIDIndex),
repo.R5[Coll[Long]].get.slice(WIDIndex + 1, watcherCount) == repoOut.R5[Coll[Long]].get.slice(WIDIndex, watcherCount - 1),
repo.R5[Coll[Long]].get.slice(WIDIndex + 1, widListSize) == repoOut.R5[Coll[Long]].get.slice(WIDIndex, widOutListSize),
blake2b256(watcherCollateral.propositionBytes) == watcherCollateralScriptHash,
)
)
Expand Down
198 changes: 198 additions & 0 deletions src/test/scala/contracts/ContractTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,28 @@ class ContractTest extends TestSuite {
})
}

property("test get permit while created permit first token is not RWT") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val prover = getProver()
val userBox = Boxes.createBoxForUser(ctx, prover.getAddress, 2e9.toLong, new ErgoToken(networkConfig._3.RSN, 300L))
val otherWID = Base16.decode(Boxes.getRandomHexString()).get
val repoBox = Boxes.createRepo(ctx, 100000, 5801L, Seq(otherWID), Seq(5800L)).convertToInputWith(Boxes.getRandomHexString(), 0)
val repoOut = Boxes.createRepo(ctx, 99900, 5901L, Seq(otherWID, repoBox.getId.getBytes), Seq(5800L, 100L))
val permitBox = Boxes.createInvalidPermitBox(ctx, 100L, repoBox.getId.getBytes)
val WID = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, Configs.minBoxValue, new ErgoToken(repoBox.getId.getBytes, 1L))
val watcherCollateral = Boxes.createWatcherCollateralBox(ctx, 1e9.toLong, 100, repoBox.getId.getBytes)
val tx = ctx.newTxBuilder().addInputs(repoBox, userBox)
.fee(Configs.fee)
.addOutputs(repoOut, permitBox, WID, watcherCollateral)
.sendChangeTo(prover.getAddress)
.build()
val signedTx = prover.sign(tx)
println(signedTx.toJson(false))
}
})
}

property("test extend permit when there is just one permit on network") {
networkConfig._1.ergoClient.execute(ctx => {
try {
Expand Down Expand Up @@ -150,6 +172,50 @@ class ContractTest extends TestSuite {
})
}

property("test extend first permit while extended permit wid has changed") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val prover = getProver()
val WID = Base16.decode(Boxes.getRandomHexString()).get
val otherWID = Base16.decode(Boxes.getRandomHexString()).get
val userBox = Boxes.createBoxForUser(ctx, prover.getAddress, 1e9.toLong, new ErgoToken(WID, 1L), new ErgoToken(networkConfig._3.RSN, 100L))
val repoBox = Boxes.createRepo(ctx, 100000, 6459L, Seq(WID, otherWID), Seq(58L, 6400L)).convertToInputWith(Boxes.getRandomHexString(), 0)
val repoOut = Boxes.createRepoWithR7(ctx, 99900, 6559L, Seq(WID, otherWID), Seq(158L, 6400L), 1)
val permitBox = Boxes.createPermitBox(ctx, 100L, otherWID)
val WIDBox = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, Configs.minBoxValue, new ErgoToken(WID, 1L))
val tx = ctx.newTxBuilder().addInputs(repoBox, userBox)
.fee(Configs.fee)
.addOutputs(repoOut, permitBox, WIDBox)
.sendChangeTo(prover.getAddress)
.build()
val signedTx = prover.sign(tx)
println(signedTx.toJson(false))
}
})
}

property("test extend first permit while extended permit first token is not RWT") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val prover = getProver()
val WID = Base16.decode(Boxes.getRandomHexString()).get
val otherWID = Base16.decode(Boxes.getRandomHexString()).get
val userBox = Boxes.createBoxForUser(ctx, prover.getAddress, 1e9.toLong, new ErgoToken(WID, 1L), new ErgoToken(networkConfig._3.RSN, 200L))
val repoBox = Boxes.createRepo(ctx, 100000, 6459L, Seq(WID, otherWID), Seq(58L, 6400L)).convertToInputWith(Boxes.getRandomHexString(), 0)
val repoOut = Boxes.createRepoWithR7(ctx, 99900, 6559L, Seq(WID, otherWID), Seq(158L, 6400L), 1)
val permitBox = Boxes.createInvalidPermitBox(ctx, 100L, WID)
val WIDBox = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, Configs.minBoxValue, new ErgoToken(WID, 1L))
val tx = ctx.newTxBuilder().addInputs(repoBox, userBox)
.fee(Configs.fee)
.addOutputs(repoOut, permitBox, WIDBox)
.sendChangeTo(prover.getAddress)
.build()
val signedTx = prover.sign(tx)
println(signedTx.toJson(false))
}
})
}

property("test extend permit while mutating other permits amount on repo") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
Expand Down Expand Up @@ -225,6 +291,35 @@ class ContractTest extends TestSuite {
})
}

property("test partially return permits when output permit WID has changed") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val prover = getProver()
val userWID = Base16.decode(Boxes.getRandomHexString()).get
val otherWID = Base16.decode(Boxes.getRandomHexString()).get
val WIDs = Seq(
Base16.decode(Boxes.getRandomHexString()).get,
otherWID,
userWID,
Base16.decode(Boxes.getRandomHexString()).get
)
val repoBox = Boxes.createRepo(ctx, 100000, 321L, WIDs, Seq(100L, 120L, 60L, 40L)).convertToInputWith(Boxes.getRandomHexString(), 0)
val permitBox = Boxes.createPermitBox(ctx, 60L, userWID).convertToInputWith(Boxes.getRandomHexString(), 0)
val WIDBox = Boxes.createBoxForUser(ctx, prover.getAddress, 1e9.toLong, new ErgoToken(userWID, 1L))
val repoOut = Boxes.createRepoWithR7(ctx, 100020, 301L, WIDs, Seq(100L, 120L, 40L, 40L), 3)
val permitOut = Boxes.createPermitBox(ctx, 40L, otherWID)
val userOut = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, 1e8.toLong, new ErgoToken(userWID, 1), new ErgoToken(networkConfig._3.RSN, 20))
val tx = ctx.newTxBuilder().addInputs(repoBox, permitBox, WIDBox)
.fee(Configs.fee)
.addOutputs(repoOut, permitOut, userOut)
.sendChangeTo(prover.getAddress)
.build()
val signedTx = prover.sign(tx)
println(signedTx.toJson(false))
}
})
}

property("test complete return permits") {
networkConfig._1.ergoClient.execute(ctx => {
var userIndex = 0
Expand Down Expand Up @@ -258,6 +353,87 @@ class ContractTest extends TestSuite {
})
}

property("test complete return permit while extending the wid list in repo") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val prover = getProver()
val WIDs = generateRandomWIDList(6)
val amounts = Seq(100L, 120L, 140L, 20L, 40L, 250L)
val userIndex = 0
val userWID = WIDs(userIndex)
val totalPermitOut = amounts.sum
val repoBox = Boxes.createRepo(ctx, 100000L, totalPermitOut + 1L, WIDs, amounts).convertToInputWith(Boxes.getRandomHexString(), 0)
val permitBox = Boxes.createPermitBox(ctx, amounts(userIndex), userWID).convertToInputWith(Boxes.getRandomHexString(), 0)
val WIDBox = Boxes.createBoxForUser(ctx, prover.getAddress, 1e9.toLong, new ErgoToken(userWID, 1L))
val outputWIDs = WIDs.take(userIndex) ++ WIDs.drop(userIndex + 1) ++ Seq(WIDs(userIndex))
val outAmounts = amounts.take(userIndex) ++ amounts.drop(userIndex + 1)
val repoOut = Boxes.createRepoWithR7(ctx, 100000L + amounts(userIndex), (totalPermitOut - amounts(userIndex)) + 1, outputWIDs, outAmounts, userIndex + 1) // 4 + first element in WID list is chain name
val userOut = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, 1e8.toLong, new ErgoToken(userWID, 1), new ErgoToken(networkConfig._3.RSN, amounts(userIndex)))
val watcherCollateral = Boxes.createWatcherCollateralBoxInput(ctx,1e9.toLong, 100, userWID)
val tx = ctx.newTxBuilder().addInputs(repoBox, permitBox, WIDBox, watcherCollateral)
.fee(Configs.fee)
.addOutputs(repoOut, userOut)
.sendChangeTo(prover.getAddress)
.build()
prover.sign(tx)
}
})
}

property("test complete return permit while extending the rwt count list in repo") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val prover = getProver()
val WIDs = generateRandomWIDList(6)
val amounts = Seq(100L, 120L, 140L, 20L, 40L, 250L)
val userIndex = 0
val userWID = WIDs(userIndex)
val totalPermitOut = amounts.sum
val repoBox = Boxes.createRepo(ctx, 100000L, totalPermitOut + 1L, WIDs, amounts).convertToInputWith(Boxes.getRandomHexString(), 0)
val permitBox = Boxes.createPermitBox(ctx, amounts(userIndex), userWID).convertToInputWith(Boxes.getRandomHexString(), 0)
val WIDBox = Boxes.createBoxForUser(ctx, prover.getAddress, 1e9.toLong, new ErgoToken(userWID, 1L))
val outputWIDs = WIDs.take(userIndex) ++ WIDs.drop(userIndex + 1)
val outAmounts = amounts.take(userIndex) ++ amounts.drop(userIndex + 1) ++ Seq(100L)
val repoOut = Boxes.createRepoWithR7(ctx, 100000L + amounts(userIndex), (totalPermitOut - amounts(userIndex)) + 1, outputWIDs, outAmounts, userIndex + 1) // 4 + first element in WID list is chain name
val userOut = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, 1e8.toLong, new ErgoToken(userWID, 1), new ErgoToken(networkConfig._3.RSN, amounts(userIndex)))
val watcherCollateral = Boxes.createWatcherCollateralBoxInput(ctx, 1e9.toLong, 100, userWID)
val tx = ctx.newTxBuilder().addInputs(repoBox, permitBox, WIDBox, watcherCollateral)
.fee(Configs.fee)
.addOutputs(repoOut, userOut)
.sendChangeTo(prover.getAddress)
.build()
prover.sign(tx)
}
})
}

property("test complete return permit while extending both rwt count and wid list in repo") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val prover = getProver()
val WIDs = generateRandomWIDList(6)
val amounts = Seq(100L, 120L, 140L, 20L, 40L, 250L)
val userIndex = 0
val userWID = WIDs(userIndex)
val totalPermitOut = amounts.sum
val repoBox = Boxes.createRepo(ctx, 100000L, totalPermitOut + 1L, WIDs, amounts).convertToInputWith(Boxes.getRandomHexString(), 0)
val permitBox = Boxes.createPermitBox(ctx, amounts(userIndex), userWID).convertToInputWith(Boxes.getRandomHexString(), 0)
val WIDBox = Boxes.createBoxForUser(ctx, prover.getAddress, 1e9.toLong, new ErgoToken(userWID, 1L))
val outputWIDs = WIDs.take(userIndex) ++ WIDs.drop(userIndex + 1) ++ Seq(WIDs(userIndex))
val outAmounts = amounts.take(userIndex) ++ amounts.drop(userIndex + 1) ++ Seq(100L)
val repoOut = Boxes.createRepoWithR7(ctx, 100000L + amounts(userIndex), (totalPermitOut - amounts(userIndex)) + 1, outputWIDs, outAmounts, userIndex + 1) // 4 + first element in WID list is chain name
val userOut = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, 1e8.toLong, new ErgoToken(userWID, 1), new ErgoToken(networkConfig._3.RSN, amounts(userIndex)))
val watcherCollateral = Boxes.createWatcherCollateralBoxInput(ctx, 1e9.toLong, 100, userWID)
val tx = ctx.newTxBuilder().addInputs(repoBox, permitBox, WIDBox, watcherCollateral)
.fee(Configs.fee)
.addOutputs(repoOut, userOut)
.sendChangeTo(prover.getAddress)
.build()
prover.sign(tx)
}
})
}

property("test complete return of last permit") {
networkConfig._1.ergoClient.execute(ctx => {
try {
Expand Down Expand Up @@ -338,6 +514,28 @@ class ContractTest extends TestSuite {
})
}

property("test create new commitment with RWT second place of permit tokens") {
networkConfig._1.ergoClient.execute(ctx => {
assertThrows[AnyRef] {
val commitment = new Commitment()
val prover = getProver()
val WID = Base16.decode(Boxes.getRandomHexString()).get
val box1 = Boxes.createBoxForUser(ctx, prover.getAddress, 1e9.toLong, new ErgoToken(WID, 1), new ErgoToken(networkConfig._3.RSN, 9))
val permit = Boxes.createPermitBox(ctx, 10L, WID).convertToInputWith(Boxes.getRandomHexString(), 0)
val permitOut = Boxes.createInvalidMixedPermitBox(ctx, 9L, WID)
val commitmentBox = Boxes.createCommitment(ctx, WID, commitment.requestId(), commitment.hash(WID), 1l)
val WIDOut = Boxes.createBoxCandidateForUser(ctx, prover.getAddress, 1e8.toLong, new ErgoToken(WID, 1))
val tx = ctx.newTxBuilder().addInputs(permit, box1)
.fee(Configs.fee)
.sendChangeTo(prover.getAddress)
.addOutputs(permitOut, commitmentBox, WIDOut)
.build()
val signedTx = prover.sign(tx)
println(signedTx.toJson(false))
}
})
}

property("test create new commitment with more than one RWT") {
networkConfig._1.ergoClient.execute(ctx => {
try {
Expand Down
32 changes: 32 additions & 0 deletions src/test/scala/testUtils/Boxes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,38 @@ object Boxes {
.build()
}

def createInvalidMixedPermitBox(ctx: BlockchainContext, RWTCount: Long, WID: Array[Byte], tokens: ErgoToken*): OutBox = {
val txB = ctx.newTxBuilder()
val tokensSeq = Seq(
new ErgoToken(networkConfig._3.RSN, RWTCount),
new ErgoToken(networkConfig._2.tokens.RWTId, RWTCount),
) ++ tokens.toSeq
txB.outBoxBuilder()
.value(Configs.minBoxValue)
.contract(contracts.WatcherPermit._1)
.tokens(tokensSeq: _*)
.registers(
ErgoValueBuilder.buildFor(Colls.fromArray(Seq(WID).map(item => Colls.fromArray(item)).toArray)),
// this value must exists in case of redeem commitment.
ErgoValueBuilder.buildFor(Colls.fromArray(Seq(Array(0.toByte)).map(item => Colls.fromArray(item)).toArray)),
)
.build()
}
def createInvalidPermitBox(ctx: BlockchainContext, RWTCount: Long, WID: Array[Byte], tokens: ErgoToken*): OutBox = {
val txB = ctx.newTxBuilder()
val tokensSeq = Seq(new ErgoToken(networkConfig._3.RSN, RWTCount)) ++ tokens.toSeq
txB.outBoxBuilder()
.value(Configs.minBoxValue)
.contract(contracts.WatcherPermit._1)
.tokens(tokensSeq: _*)
.registers(
ErgoValueBuilder.buildFor(Colls.fromArray(Seq(WID).map(item => Colls.fromArray(item)).toArray)),
// this value must exists in case of redeem commitment.
ErgoValueBuilder.buildFor(Colls.fromArray(Seq(Array(0.toByte)).map(item => Colls.fromArray(item)).toArray)),
)
.build()
}

def createFraudBox(ctx: BlockchainContext, WID: Array[Byte], RWTCount: Long): OutBox = {
val txB = ctx.newTxBuilder()
txB.outBoxBuilder()
Expand Down

0 comments on commit 11c5973

Please sign in to comment.