Skip to content

Commit

Permalink
Merge pull request #62 from ergoplatform/develop
Browse files Browse the repository at this point in the history
Release v3.3.2 (with Sigma v3.3.1 and ergo-wallet v3.3.3)
  • Loading branch information
aslesarenko authored Oct 7, 2020
2 parents 2adc7a4 + 3e9e880 commit 50fc495
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.ergoplatform.appkit

import org.ergoplatform.{ErgoAddressEncoder, Pay2SAddress}
import org.scalatest.{Matchers, PropSpec}
import org.scalatest.{PropSpec, Matchers}
import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks
import scorex.util.encode.Base16
import sigmastate.eval._
import sigmastate.interpreter.CryptoConstants
import sigmastate.serialization.ErgoTreeSerializer
import special.sigma.GroupElement
import JavaHelpers._
import java.util.{List => JList}

import org.ergoplatform.appkit.Parameters.MinFee

class ChangeOutputSpec extends PropSpec with Matchers
with ScalaCheckDrivenPropertyChecks
Expand Down Expand Up @@ -97,7 +98,7 @@ class ChangeOutputSpec extends PropSpec with Matchers
}
}

property("NoTokenChangeOutput") {
property("NoTokenChangeOutput + token burning") {
val ergoClient = createMockedErgoClient(MockData(Nil, Nil))
val g: GroupElement = CryptoConstants.dlogGroup.generator
val x = BigInt("187235612876647164378132684712638457631278").bigInteger
Expand All @@ -120,6 +121,8 @@ class ChangeOutputSpec extends PropSpec with Matchers
)).build().convertToInputWith("f9e5ce5aa0d95f5d54a7bc89c46730d9662397067250aa18a0039631c0f5b809", 0)

val tokenId = input0.getId.toString
val tokenAmount = 5000000000L
val tokenAmountToBurn = 2000000000L

val ph = ctx.createPreHeader()
.height(ctx.getHeight + 1)
Expand All @@ -128,7 +131,7 @@ class ChangeOutputSpec extends PropSpec with Matchers
val txB = ctx.newTxBuilder().preHeader(ph) // for issuing token
val tokenBox = txB.outBoxBuilder
.value(15000000) // value of token box, doesn't really matter
.tokens(new ErgoToken(tokenId, 5000000000L)) // amount of token issuing
.tokens(new ErgoToken(tokenId, tokenAmount)) // amount of token issuing
.contract(ctx.compileContract( // contract of the box containing tokens, just has to be spendable
ConstantsBuilder.empty(),
"{sigmaProp(1 < 2)}"
Expand All @@ -148,6 +151,43 @@ class ChangeOutputSpec extends PropSpec with Matchers
val outputs = signed.getOutputsToSpend
assert(outputs.size == 2)
println(signed.toJson(false))
val boxWithToken = outputs.get(0)
assert(boxWithToken.getTokens.size() == 1)

// move Ergs from boxWithToken and burn tokens in the transaction
{
val expectedChange = MinFee
val txB = ctx.newTxBuilder()
val ergAmountToSend = boxWithToken.getValue - MinFee - expectedChange
val out = txB.outBoxBuilder
.value(ergAmountToSend)
.contract(ctx.compileContract( // contract of the box containing tokens, just has to be spendable
ConstantsBuilder.empty(), "{sigmaProp(1 < 2)}"
))
.build()
val unsigned = txB
.boxesToSpend(IndexedSeq(boxWithToken).convertTo[JList[InputBox]])
.outputs(out)
.tokensToBurn(new ErgoToken(tokenId, tokenAmountToBurn))
.fee(MinFee)
.sendChangeTo(changeAddr)
.build()
val signed = ctx.newProverBuilder().build().sign(unsigned)
val outputs = signed.getOutputsToSpend
assert(outputs.size == 3)
val out0 = outputs.get(0)
val fee = outputs.get(1)
val change = outputs.get(2)
out0.getValue shouldBe ergAmountToSend
out0.getTokens.size() shouldBe 0

fee.getValue shouldBe MinFee
fee.getTokens.size shouldBe 0

change.getValue shouldBe expectedChange
change.getTokens.size() shouldBe 1
change.getTokens.get(0).getValue shouldBe (tokenAmount - tokenAmountToBurn)
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ assemblyMergeStrategy in assembly := {
lazy val allConfigDependency = "compile->compile;test->test"

val sigmaStateVersion = "3.3.1"
val ergoWalletVersion = "v3.2.3-11f8615f-SNAPSHOT"
val ergoWalletVersion = "v3.3.3-c42d8b5b-SNAPSHOT"
lazy val sigmaState = ("org.scorexfoundation" %% "sigma-state" % sigmaStateVersion).force()
.exclude("ch.qos.logback", "logback-classic")
.exclude("org.scorexfoundation", "scrypto")
Expand Down
33 changes: 19 additions & 14 deletions common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import java.util.{List => JList, Map => JMap}

import sigmastate.utils.Helpers._ // don't remove, required for Scala 2.11
import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
import org.ergoplatform.wallet.TokensMap
import scorex.util.encode.Base16
import sigmastate.basics.DLogProtocol.ProveDlog
import sigmastate.basics.{ProveDHTuple, DiffieHellmanTupleProverInput}
Expand Down Expand Up @@ -79,22 +80,23 @@ object Iso extends LowPriorityIsos {
override def from(t: (TokenId, Long)): ErgoToken = new ErgoToken(t._1, t._2)
}

implicit val isoJListErgoTokenToMapPair: Iso[JList[ErgoToken], mutable.LinkedHashMap[ModifierId, Long]] =
implicit val isoJListErgoTokenToMapPair: Iso[JList[ErgoToken], mutable.LinkedHashMap[ModifierId, Long]] =
new Iso[JList[ErgoToken], mutable.LinkedHashMap[ModifierId, Long]] {
override def to(a: JList[ErgoToken]): mutable.LinkedHashMap[ModifierId, Long] = {
import JavaHelpers._
val lhm = new mutable.LinkedHashMap[ModifierId, Long]()
a.convertTo[IndexedSeq[(TokenId, Long)]]
.map(t => bytesToId(t._1) -> t._2)
.foldLeft(lhm)(_ += _)
}
override def from(t: mutable.LinkedHashMap[ModifierId, Long]): JList[ErgoToken] = {
import JavaHelpers._
val pairs: IndexedSeq[(TokenId, Long)] = t.toIndexedSeq
.map(t => (Digest32 @@ idToBytes(t._1)) -> t._2)
pairs.convertTo[JList[ErgoToken]]
override def to(a: JList[ErgoToken]): mutable.LinkedHashMap[ModifierId, Long] = {
import JavaHelpers._
val lhm = new mutable.LinkedHashMap[ModifierId, Long]()
a.convertTo[IndexedSeq[(TokenId, Long)]]
.map(t => bytesToId(t._1) -> t._2)
.foldLeft(lhm)(_ += _)
}

override def from(t: mutable.LinkedHashMap[ModifierId, Long]): JList[ErgoToken] = {
import JavaHelpers._
val pairs: IndexedSeq[(TokenId, Long)] = t.toIndexedSeq
.map(t => (Digest32 @@ idToBytes(t._1)) -> t._2)
pairs.convertTo[JList[ErgoToken]]
}
}
}

implicit val isoErgoTypeToSType: Iso[ErgoType[_], SType] = new Iso[ErgoType[_], SType] {
override def to(et: ErgoType[_]): SType = Evaluation.rtypeToSType(et.getRType)
Expand Down Expand Up @@ -347,6 +349,9 @@ object JavaHelpers {
DiffieHellmanTupleProverInput(x, dht)
}

def createTokensMap(linkedMap: mutable.LinkedHashMap[ModifierId, Long]): TokensMap = {
linkedMap.toMap
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public interface SignedTransaction {
List<SignedInput> getSignedInputs();

/**
* Outputs of this transaction represented as {@link InputBox} objects read to be spent in the next
* Outputs of this transaction represented as {@link InputBox} objects ready to be spent in the next
* chained transaction.
* This method can be used to create a chain of transactions. Thus {@code tx1.getOutputsToSpend()} returns
* a list of boxes which are ready to be included as input boxes to a new tx2.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ public interface UnsignedTransactionBuilder {
*/
UnsignedTransactionBuilder fee(long feeAmount);

/**
* Configures amounts for tokens to be burnt.
* Each Ergo box can store zero or more tokens (aka assets).
* In contrast to strict requirement on ERG balance between transaction inputs and outputs,
* the amounts of output tokens can be less then the amounts of input tokens.
* This is interpreted as token burning i.e. reducing the total amount of tokens in
* circulation in the blockchain.
* Note, once issued/burnt, the amount of tokens in circulation cannot be increased.
*
* @param tokens one or more tokens to be burnt as part of the transaction.
* @see ErgoToken
*/
UnsignedTransactionBuilder tokensToBurn(ErgoToken... tokens);

/**
* Adds change output to the specified address if needed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import org.ergoplatform.wallet.transactions.TransactionBuilder;
import org.ergoplatform.wallet.boxes.DefaultBoxSelector$;
import org.ergoplatform.wallet.boxes.BoxSelector;
import scala.Option;
import scala.collection.IndexedSeq;
import scala.collection.immutable.Map;
import special.collection.Coll;
import special.sigma.Header;
import special.sigma.PreHeader;
Expand All @@ -29,6 +29,7 @@ public class UnsignedTransactionBuilderImpl implements UnsignedTransactionBuilde
ArrayList<ErgoBoxCandidate> _outputCandidates = new ArrayList<>();
private List<InputBoxImpl> _inputBoxes;
private List<InputBoxImpl> _dataInputBoxes = new ArrayList<>();
private List<ErgoToken> _tokensToBurn = new ArrayList<>();
private long _feeAmount;
private ErgoAddress _changeAddress;
private PreHeaderImpl _ph;
Expand Down Expand Up @@ -86,6 +87,12 @@ public UnsignedTransactionBuilder fee(long feeAmount) {
return this;
}

@Override
public UnsignedTransactionBuilder tokensToBurn(ErgoToken... tokens) {
_tokensToBurn.addAll(Stream.of(tokens).collect(Collectors.toList()));
return this;
}

private void appendOutputs(OutBox... outputs) {
ErgoBoxCandidate[] boxes =
Stream.of(outputs).map(c -> ((OutBoxImpl)c).getErgoBoxCandidate()).toArray(n -> new ErgoBoxCandidate[n]);
Expand All @@ -101,8 +108,12 @@ public UnsignedTransactionBuilder sendChangeTo(ErgoAddress changeAddress) {

@Override
public UnsignedTransaction build() {
List<ErgoBox> boxesToSpend = _inputBoxes.stream().map(b -> b.getErgoBox()).collect(Collectors.toList());
List<ErgoBox> dataInputBoxes = _dataInputBoxes.stream().map(b -> b.getErgoBox()).collect(Collectors.toList());
List<ErgoBox> boxesToSpend = _inputBoxes.stream()
.map(b -> b.getErgoBox())
.collect(Collectors.toList());
List<ErgoBox> dataInputBoxes = _dataInputBoxes.stream()
.map(b -> b.getErgoBox())
.collect(Collectors.toList());
IndexedSeq<DataInput> dataInputs = JavaHelpers.toIndexedSeq(_dataInputs);

checkState(_feeAmount > 0, "Fee amount should be defined (using fee() method).");
Expand All @@ -111,16 +122,20 @@ public UnsignedTransaction build() {

IndexedSeq<ErgoBoxCandidate> outputCandidates = JavaHelpers.toIndexedSeq(_outputCandidates);
IndexedSeq<ErgoBox> inputBoxes = JavaHelpers.toIndexedSeq(boxesToSpend);
Map<String, Object> burnTokens = JavaHelpers.createTokensMap(
Iso$.MODULE$.isoJListErgoTokenToMapPair().to(_tokensToBurn)
);
BoxSelector boxSelector = DefaultBoxSelector$.MODULE$;
UnsignedErgoLikeTransaction tx = TransactionBuilder.buildUnsignedTx(
inputBoxes,
dataInputs,
outputCandidates,
dataInputs,
outputCandidates,
_ctx.getHeight(),
_feeAmount,
_changeAddress,
MinChangeValue,
_feeAmount,
_changeAddress,
MinChangeValue,
Parameters.MinerRewardDelay,
burnTokens,
boxSelector).get();
ErgoLikeStateContext stateContext = createErgoLikeStateContext();

Expand Down

0 comments on commit 50fc495

Please sign in to comment.