From 4e6d893ca779920917f9497e51c49baa9fb67331 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 4 Jun 2021 23:39:38 +0300 Subject: [PATCH 01/10] README.md updated (part 1) --- README.md | 77 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index c2e55e3a..9f182cb8 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,5 @@ # Appkit: A Library for Polyglot Development of Ergo Applications -**NOTE: The code in Appkit is experimental. All APIs may change and/or -be removed without notice.** - ## Contents - [Introduction](#introduction) - [Using from Java](#using-from-java) @@ -18,20 +15,27 @@ be removed without notice.** ## Introduction [Ergo](https://ergoplatform.org/en/) is a resilient blockchain platform for contractual money. In addition to [Bitcoin](https://bitcoin.org/en/)-like -blockchain architecture Ergo provides advanced contractual capabilities, which -are not possible in Bitcoin. +blockchain architecture Ergo provides advanced contractual capabilities based on +eUTXO model, which are not possible in Bitcoin. Because of these capabilities numerous decentralized applications become -possible(see [ErgoScript](https://ergoplatform.org/docs/ErgoScript.pdf), +possible (see also [ErgoScript](https://ergoplatform.org/docs/ErgoScript.pdf) and [Advanced ErgoScript -examples](https://ergoplatform.org/docs/AdvancedErgoScriptTutorial.pdf), -[CrowdFunding](https://ergoplatform.org/en/blog/2019_09_06_crowdfund/), -[Auctions On Ergo](https://www.ergoforum.org/t/auctions-on-ergo/122), -[Interest-Free Loan -Contract](https://www.ergoforum.org/t/interest-free-loan-contract/67) etc). Ergo -applications can avoid centralization of trust and instead rely on trust-less -truly decentralized protocols which are implemented using Ergo contracts -deployed on Ergo blockchain. +examples](https://ergoplatform.org/docs/AdvancedErgoScriptTutorial.pdf)): + - [SigmaUSD](https://sigmausd.io/) - the first UTxO-based stable coin. + - [ErgoMixer](https://github.com/ergoMixer/ergoMixBack) - The first working + non-custodial, programmable, non-interactive mixer in the cryptocurrency space. + - [ErgoAuctions](https://ergoauctions.org) - a decentralized auction house, + secure and easy to use. A simple way to sell or buy Ergo's tokens, artworks, + NFTs, etc. +- [ErgoDex](https://ergodex.io/) - Decentralized exchange on Ergo and Cardano +- [CrowdFunding](https://ergoplatform.org/en/blog/2019_09_06_crowdfund/), +- [Interest-Free Loan +Contract](https://www.ergoforum.org/t/interest-free-loan-contract/67) etc). + +Ergo Applications can avoid centralization of trust and instead rely on +trust-less truly decentralized protocols which are implemented using Ergo +contracts deployed on Ergo blockchain. By its very nature, Ergo applications are both decentralized and cross-platform ranging from web applications running in browsers, mobile applications running @@ -41,16 +45,19 @@ to services deployed in cloud servers. That said, it is very desirable to provide consistent programming model and APIs to facilitate all those application scenarios and platforms. -Appkit library is based on [GraalVM](https://www.graalvm.org) - a novel next -generation approach to implement software which is reusable across several -programming languages and execution environments (see [motivation for using -Graal](#why-graal) below). - -Appkit has idiomatic Java API and is written in Java/Scala. It is a thin wrapper -around core components provided by [ErgoScript +Ergo Appkit has idiomatic Java API and is written in Java/Scala. It is a thin +wrapper around core components provided by [ErgoScript interpreter](https://github.com/ScorexFoundation/sigmastate-interpreter) and [Ergo protocol](https://github.com/ergoplatform/ergo) implementations which are -written in Scala. +written in Scala. It is +[published](https://mvnrepository.com/artifact/org.ergoplatform/ergo-appkit) on +maven repository and cross compiled to both Java 7 and Java 8+ jars. + +The Appkit library is compatible with [GraalVM](https://www.graalvm.org) - a +novel next generation approach to implement software which is reusable across +several programming languages and execution environments. For example if Node.js +application is run on GraalVM, then it can use Appkit to interact with Ergo +Blockchain (see [motivation for using Graal](#why-graal) below). Using Appkit Ergo applications can be written in one of the languages supported by GraalVM (i.e. Java, JavaScript, C/C++, Python, Ruby, R) and using this @@ -59,7 +66,9 @@ programming model provided by Appkit. In addition Appkit based Ergo applications can be compiled into native code using [native-image ahead of time compiler](https://www.graalvm.org/docs/reference-manual/native-image/) and then executed without Java VM with very fast startup time and lower runtime memory -overhead compared to a Java VM. +overhead compared to a Java VM. For example this allows to create very +responsive command line utility applications such as +[ergo-tool](https://github.com/ergoplatform/ergo-tool). Please follow the [setup instructions](#setup) to get started. @@ -68,11 +77,12 @@ Please follow the [setup instructions](#setup) to get started. ### Using from Java Among other things, Appkit library allows to communicate with Ergo nodes via -REST API. Let's see how we can write ErgoTool - a simple Java console application (similar to +[REST API](https://github.com/ergoplatform/ergo/blob/master/src/main/resources/api/openapi.yaml). +Let's see how we can write ErgoTool - a simple Java console application (similar to [ergo-tool](https://github.com/ergoplatform/ergo-tool) utility) which uses Appkit library. ErgoTool allows to create and send a new transaction -to an Ergo node which, for example, can be started locally and thus available at -`http://localhost:9052/`. Suppose we [set up a full +to an any existing Ergo node on the network which. A new node can also be started +locally and thus available at `http://localhost:9052/`. Suppose we [set up a full node](https://github.com/ergoplatform/ergo/wiki/Set-up-a-full-node) and started it using the following command. ```shell @@ -80,12 +90,12 @@ $ java -jar -Xmx4G target/scala-2.12/ergo-4.0.8.jar --testnet -c ergo-testnet.co ``` We will need some configuration parameters which can be loaded from -`ergotool.json` file which looks like this +[ergotool.json](ergotool.json) file which looks like this ```json { "node": { "nodeApi": { - "apiUrl": "http://localhost:9051/", + "apiUrl": "http://139.59.29.87:9053", "apiKey": "82344a18c24adc42b78f52c58facfdf19c8cc38858a5f22e68070959499076e1" }, "wallet": { @@ -93,15 +103,22 @@ We will need some configuration parameters which can be loaded from "password": "", "mnemonicPassword": "" }, - "networkType": "TESTNET" + "networkType": "MAINNET" + }, + "parameters": { + "newBoxSpendingDelay": "30" } } ``` + Here `apiKey` is the secret key required for API authentication which can be obtained as described [here](https://github.com/ergoplatform/ergo/wiki/Ergo-REST-API#setting-an-api-key). And mnemonic is the secret phrase obtained during [setup of a new -wallet](https://github.com/ergoplatform/ergo/wiki/Wallet-documentation). +wallet](https://github.com/ergoplatform/ergo/wiki/Wallet-documentation) or if +you don't want to setup your node using ergo-tool's +[mnemonic](https://github.com/ergoplatform/ergo-tool#supported-commands) +command. Our example app also reads the amount of NanoErg to put into a new box from command line arguments ```java From 930a3458a33fd6c010db7df484c0906f1a324a17 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 5 Jun 2021 16:27:13 +0300 Subject: [PATCH 02/10] README.md updated (part 2) --- README.md | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9f182cb8..2b86a8a3 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,7 @@ that box. ```java // the only way to create transaction is using builder obtained from the context -// the builder keeps relationship with the context to access nessary blockchain data. +// the builder keeps relationship with the context to access necessary blockchain data. UnsignedTransactionBuilder txB = ctx.newTxBuilder(); // create new box using new builder obtained from the transaction builder @@ -202,16 +202,16 @@ If no such constants are used, then `ConstantsBuilder.empty()` can be passed. In this specific case we pass public key of the `prover` for `pkOwner` placeholder of the script meaning the box can be spend only by the owner of the -Ergo node we are working with. +public key from the `wallet` section of [ergotool.json](ergotool.json). -Next create an unsigned transaction using all the data collected so far. +Next we create an unsigned transaction using all the data collected so far. ```java // tell transaction builder which boxes we are going to spend, which outputs // to create, amount of transaction fees and address for change coins. UnsignedTransaction tx = txB.boxesToSpend(boxes.get()) .outputs(newBox) .fee(Parameters.MinFee) - .sendChangeTo(prover.getP2PKAddress()) + .sendChangeTo(prover.getP2PKAddress()) // i.e. back to the wallet's pk .build(); ``` @@ -223,7 +223,7 @@ is not really used here. ```java SignedTransaction signed = prover.sign(tx); String txId = ctx.sendTransaction(signed); -return signed.toJson(true); +return signed.toJson(/*prettyPrint=*/true, /*formatJson=*/true); ``` As the last step we serialize signed transaction into Json with turned on pretty printing. Please see the [full source @@ -232,7 +232,8 @@ example. ## Using from other languages In additiona to Java, Appkit can be used to write Ergo applications in Scala, JavaScript, -Python and Ruby and run those applications under GraalVM. +Python and Ruby and run those applications under GraalVM, which support cross +language interoperability. Please see [examples](https://github.com/aslesarenko/ergo-appkit-examples). ## Repository organization @@ -264,8 +265,8 @@ edition should be enough for Ergo Appkit library. First you need to download an archive with the [latest release](https://github.com/oracle/graal/releases) of GraalVM (e.g. -`graalvm-ce-darwin-amd64-19.2.1.tar.gz`) for MacOS and put the programs from it -onto the `$PATH`. +`graalvm-ce-darwin-amd64-19.2.1.tar.gz` at the time of writing) for MacOS and +put the programs from it onto the `$PATH`. ```shell $ cd @@ -296,8 +297,10 @@ GraalVM JavaScript (GraalVM CE Native 19.2.1) ### Building the Appkit jar file -At the moment Appkit is not published at public servers, so the whole repository -needs to be clonned and Appkit jar file published locally in the Ivy repository. +Appkit is +[published](https://mvnrepository.com/artifact/org.ergoplatform/ergo-appkit), +however, you can clone the whole repository build it and published locally in +the Ivy repository. ```shell $ git clone https://github.com/ergoplatform/ergo-appkit.git @@ -305,7 +308,7 @@ $ cd ergo-appkit $ sbt publishLocal ``` -## Why Polyglot? +## How GraalVM can be used? After many years of research and development GraalVM project has matured enough end became [production ready](https://medium.com/graalvm/announcing-graalvm-19-4590cf354df8). @@ -331,8 +334,9 @@ languages. - All the code from [ErgoScript interpreter](https://github.com/ScorexFoundation/sigmastate-interpreter/pulls), -[ergo-wallet](https://github.com/ergoplatform/ergo-wallet) and this Appkit is compatible with -`native-image` and can be compiled into either native application or shared library. +[ergo-wallet](https://github.com/ergoplatform/ergo-wallet) and this Appkit +library is compatible with `native-image` and can be compiled into either native +application or shared library. - Appkit API interfaces can be used from JavaScript and Python by design. They can also be [used from C/C++](https://github.com/aslesarenko/ergo-appkit-examples/tree/master/c-examples) . @@ -346,14 +350,15 @@ overhead when running scripts ## Projects that use Appkit Appkit is a foundational non-opinionated libarary which can be used to create other -libraries, Apps and tools. Here is the list of project which are using Appkit. - -- [ergo-tool](https://github.com/ergoplatform/ergo-tool) -- [ergo-appkit-examples](https://github.com/aslesarenko/ergo-appkit-examples) -- [ergo-android](https://github.com/aslesarenko/ergo-android) -- [ErgoMixer](https://github.com/ergoMixer/ergoMixBack) -- [ergo-playgrounds](https://github.com/ergoplatform/ergo-playgrounds) -- [Gravity-Ergo-Proxy](https://github.com/mhssamadani/Gravity-Ergo-Proxy) -- [Kiosk](https://github.com/scalahub/Kiosk) -- [ergo-mixer-demo](https://github.com/anon92048/ergo-mixer-demo) +libraries, Apps and tools. Here is the list of projects which use Appkit. + +- [ergo-tool](https://github.com/ergoplatform/ergo-tool) - a Command Line Interface application for Ergo +- [ergo-appkit-examples](https://github.com/aslesarenko/ergo-appkit-examples) - Ergo Appkit Examples +- [ergo-android](https://github.com/aslesarenko/ergo-android) - Example Android application +- [ErgoMixer](https://github.com/ergoMixer/ergoMixBack) - a web application for mixing ergs and tokens based on Ergo platform +- [ergo-playgrounds](https://github.com/ergoplatform/ergo-playgrounds) - Run contracts + off-chain code in the browser +- [Gravity-Ergo-Proxy](https://github.com/mhssamadani/Gravity-Ergo-Proxy) - Wrapper of Ergo Node to work with Gravity.tech +- [ergo-jde](https://github.com/ergoplatform/ergo-jde) - JSON dApp Environment (JDE) +- [Kiosk](https://github.com/scalahub/Kiosk) - a library on top of Ergo-Appkit for interacting with the Ergo Blockchain +- [ergo-mixer-demo](https://github.com/anon92048/ergo-mixer-demo) - a non-interactive (and non-custodial) mixing scheme on top of the Ergo Platform blockchain - Add your project here by creating a PR From fbf9dfc9cf5d58bf486fb8cbab9ef127d4ea1fd7 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 5 Jun 2021 16:34:35 +0300 Subject: [PATCH 03/10] README.md updated (part 3) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2b86a8a3..190add2e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - [Prerequisites](#prerequisites) - [Install GraalVM Community Edition on MacOS](#install-graalvm-community-edition-on-macos) - [Building the Appkit jar file](#building-the-appkit-jar-file) -- [Why Polyglot](#why-polyglot) +- [Why Polyglot](#how-graalvm-can-be-used) - [Projects that use Appkit](#projects-that-use-appkit) ## Introduction @@ -29,9 +29,9 @@ examples](https://ergoplatform.org/docs/AdvancedErgoScriptTutorial.pdf)): secure and easy to use. A simple way to sell or buy Ergo's tokens, artworks, NFTs, etc. - [ErgoDex](https://ergodex.io/) - Decentralized exchange on Ergo and Cardano -- [CrowdFunding](https://ergoplatform.org/en/blog/2019_09_06_crowdfund/), +- [CrowdFunding](https://ergoplatform.org/en/blog/2019_09_06_crowdfund/) - a way of raising capital through the collective efforts of individuals - [Interest-Free Loan -Contract](https://www.ergoforum.org/t/interest-free-loan-contract/67) etc). +Contract](https://www.ergoforum.org/t/interest-free-loan-contract/67) etc. Ergo Applications can avoid centralization of trust and instead rely on trust-less truly decentralized protocols which are implemented using Ergo From e289f69d58220d2e089761d032e001f589c72291 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 12 Jun 2021 15:49:39 +0300 Subject: [PATCH 04/10] support EIP-3 addresses --- .../org/ergoplatform/appkit/AddressSpec.scala | 11 ++++- .../java/org/ergoplatform/appkit/Address.java | 42 +++++++++++++++++++ .../org/ergoplatform/appkit/JavaHelpers.scala | 13 ++++-- .../appkit/AppkitTestingCommon.scala | 16 +++++++ .../appkit/SecretStorageSpec.scala | 14 +++---- 5 files changed, 85 insertions(+), 11 deletions(-) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/AddressSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/AddressSpec.scala index 706b967c..9bb40bf7 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/AddressSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/AddressSpec.scala @@ -20,13 +20,22 @@ class AddressSpec extends PropSpec with Matchers with ScalaCheckDrivenPropertyCh } property("Address fromMnemonic") { - val mnemonic = SecretString.create("slow silly start wash bundle suffer bulb ancient height spin express remind today effort helmet") val addr = Address.fromMnemonic(NetworkType.TESTNET, mnemonic, SecretString.empty()) addr.toString shouldBe addrStr val addr2 = Address.fromMnemonic(NetworkType.MAINNET, mnemonic, SecretString.empty()) addr2.toString shouldNot be (addrStr) } + property("Address createEip3Address") { + val addr = Address.fromMnemonic(NetworkType.MAINNET, mnemonic, SecretString.empty()) + val firstEip3Addr = Address.createEip3Address(0, NetworkType.MAINNET, mnemonic, SecretString.empty()) + firstEip3Addr.toString shouldBe firstEip3AddrStr + addr.toString shouldNot be (firstEip3AddrStr) + + val secondEip3Addr = Address.createEip3Address(1, NetworkType.MAINNET, mnemonic, SecretString.empty()) + secondEip3Addr.toString shouldBe secondEip3AddrStr + } + property("create Address from ErgoTree with DHT") { val tree = "100207036ba5cfbc03ea2471fdf02737f64dbcd58c34461a7ec1e586dcd713dacbf89a120400d805d601db6a01ddd6027300d603b2a5730100d604e4c672030407d605e4c672030507eb02ce7201720272047205ce7201720472027205" implicit val encoder: ErgoAddressEncoder = ErgoAddressEncoder.apply(NetworkType.MAINNET.networkPrefix); diff --git a/common/src/main/java/org/ergoplatform/appkit/Address.java b/common/src/main/java/org/ergoplatform/appkit/Address.java index 7a52f04c..c176a142 100644 --- a/common/src/main/java/org/ergoplatform/appkit/Address.java +++ b/common/src/main/java/org/ergoplatform/appkit/Address.java @@ -5,6 +5,8 @@ import org.ergoplatform.ErgoAddressEncoder; import org.ergoplatform.P2PKAddress; import org.ergoplatform.Pay2SAddress; +import org.ergoplatform.wallet.secrets.DerivationPath; +import org.ergoplatform.wallet.secrets.ExtendedPublicKey; import org.ergoplatform.wallet.secrets.ExtendedSecretKey; import scala.util.Try; import scorex.util.encode.Base58; @@ -130,6 +132,16 @@ public static Address fromMnemonic(NetworkType networkType, Mnemonic mnemonic) { return fromMnemonic(networkType, mnemonic.getPhrase(), mnemonic.getPassword()); } + /** + * Creates an {@link Address} from the given mnemonic using `m / 0` derivation path + * (corresponds to master key). The returned address is not compliant with + * EIP-3. + * + * @param networkType mainnet or testnet network + * @param mnemonic mnemonic (e.g. 15 words) phrase + * @param mnemonicPass optional (i.e. it can be empty) mnemonic password which is + * necessary to know in order to restore the secrets + */ public static Address fromMnemonic( NetworkType networkType, SecretString mnemonic, SecretString mnemonicPass) { ExtendedSecretKey masterKey = JavaHelpers.seedToMasterKey(mnemonic, mnemonicPass); @@ -139,6 +151,36 @@ public static Address fromMnemonic( return new Address(p2pkAddress); } + /** + * Creates an {@link Address} from the given mnemonic using EIP-3 + * derivation path (`m / 44' / 429' / 0' / 0 / 0`) + * The returned address is compliant with EIP-3. + * + * @param index the last index in the path (zero based) + * @param networkType mainnet or testnet network + * @param mnemonic mnemonic (e.g. 15 words) phrase + * @param mnemonicPass optional (i.e. it can be empty) mnemonic password which is + * necessary to know in order to restore the secrets + */ + public static Address createEip3Address( + int index, + NetworkType networkType, + SecretString mnemonic, + SecretString mnemonicPass) { + ExtendedSecretKey rootSecret = JavaHelpers.seedToMasterKey(mnemonic, mnemonicPass); + + // Let's use "m/44'/429'/0'/0/index" path (this path is compliant with EIP-3 which + // is BIP-44 for Ergo) + DerivationPath path = JavaHelpers.eip3DerivationWithLastIndex(index); + ExtendedSecretKey secretKey = (ExtendedSecretKey)rootSecret.derive(path); + ExtendedPublicKey pubkey = secretKey.publicKey(); + P2PKAddress p2pkAddress = JavaHelpers.createP2PKAddress( + pubkey.key(), + networkType.networkPrefix); + return new Address(p2pkAddress); + } + public static Address fromErgoTree(Values.ErgoTree ergoTree, NetworkType networkType) { Pay2SAddress p2sAddress = JavaHelpers.createP2SAddress(ergoTree, networkType.networkPrefix); return new Address(p2sAddress); diff --git a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala index 880f4644..4170b618 100644 --- a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala +++ b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala @@ -1,6 +1,6 @@ package org.ergoplatform.appkit -import org.ergoplatform.wallet.secrets.ExtendedSecretKey +import org.ergoplatform.wallet.secrets.{ExtendedSecretKey, DerivationPath} import scalan.RType import special.collection.Coll import com.google.common.base.{Preconditions, Strings} @@ -16,7 +16,7 @@ import scorex.crypto.hash.Digest32 import org.ergoplatform.wallet.mnemonic.{Mnemonic => WMnemonic} import org.ergoplatform.settings.ErgoAlgos import sigmastate.lang.Terms.ValueOps -import sigmastate.eval.{CompiletimeIRContext, Evaluation, Colls, CostingSigmaDslBuilder, CPreHeader, Extensions} +import sigmastate.eval.{CompiletimeIRContext, Evaluation, Colls, CostingSigmaDslBuilder, CPreHeader} import sigmastate.eval.Extensions._ import special.sigma.{AnyValue, AvlTree, Header, GroupElement} import java.util @@ -27,7 +27,7 @@ import java.util.{Map => JMap, List => JList} import sigmastate.utils.Helpers._ // don't remove, required for Scala 2.11 import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix import org.ergoplatform.appkit.Iso.{isoErgoTokenToPair, JListToColl} -import org.ergoplatform.wallet.TokensMap +import org.ergoplatform.wallet.{TokensMap, Constants} import scorex.util.encode.Base16 import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.{ProveDHTuple, DiffieHellmanTupleProverInput} @@ -440,6 +440,13 @@ object JavaHelpers { map.toMap -> assetsNum } + /** Creates a new EIP-3 derivation path with the given last index. + * The resulting path corresponds to `m/44'/429'/0'/0/index` path. + */ + def eip3DerivationWithLastIndex(index: Int) = { + val firstPath = Constants.eip3DerivationPath + DerivationPath(firstPath.decodedPath.dropRight(1) :+ index, firstPath.publicBranch) + } } diff --git a/common/src/test/scala/org/ergoplatform/appkit/AppkitTestingCommon.scala b/common/src/test/scala/org/ergoplatform/appkit/AppkitTestingCommon.scala index 9d246767..378efd0b 100644 --- a/common/src/test/scala/org/ergoplatform/appkit/AppkitTestingCommon.scala +++ b/common/src/test/scala/org/ergoplatform/appkit/AppkitTestingCommon.scala @@ -1,5 +1,21 @@ package org.ergoplatform.appkit trait AppkitTestingCommon { + /** The mnemonic used in tests and test vectors. */ + val mnemonic = SecretString.create("slow silly start wash bundle suffer bulb ancient height spin express remind today effort helmet") + + /** Testnet address generated from `mnemonic` and corresponding to master key. + * (i.e. `m / 0` derivation path). + */ val addrStr = "3WzR39tWQ5cxxWWX6ys7wNdJKLijPeyaKgx72uqg9FJRBCdZPovL" + + /** Mainnet address generated from `mnemonic` and corresponding to first EIP-3 address.. + * (i.e. `m/44'/429'/0'/0/0` derivation path). + */ + val firstEip3AddrStr = "9eatpGQdYNjTi5ZZLK7Bo7C3ms6oECPnxbQTRn6sDcBNLMYSCa8" + + /** Mainnet address generated from `mnemonic` and corresponding to second EIP-3 address.. + * (i.e. `m/44'/429'/0'/0/1` derivation path). + */ + val secondEip3AddrStr = "9iBhwkjzUAVBkdxWvKmk7ab7nFgZRFbGpXA9gP6TAoakFnLNomk" } diff --git a/common/src/test/scala/org/ergoplatform/appkit/SecretStorageSpec.scala b/common/src/test/scala/org/ergoplatform/appkit/SecretStorageSpec.scala index ac62e13b..0bac80b1 100644 --- a/common/src/test/scala/org/ergoplatform/appkit/SecretStorageSpec.scala +++ b/common/src/test/scala/org/ergoplatform/appkit/SecretStorageSpec.scala @@ -7,24 +7,24 @@ import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks class SecretStorageSpec extends PropSpec with Matchers with ScalaCheckDrivenPropertyChecks with AppkitTestingCommon { - val mnemonic = Mnemonic.create("phrase".toCharArray, "mnemonic pass".toCharArray) + val mnemonicWithPassword = Mnemonic.create("phrase".toCharArray, "mnemonic pass".toCharArray) val encryptionPass = "encryption pass" property("create from mnemonic") { - withNewStorageFor(mnemonic, encryptionPass) { storage => + withNewStorageFor(mnemonicWithPassword, encryptionPass) { storage => storage.isLocked shouldBe true storage.getFile().exists() shouldBe true } } property("unlocked by password with ") { - withNewStorageFor(mnemonic, encryptionPass) { storage => + withNewStorageFor(mnemonicWithPassword, encryptionPass) { storage => storage.unlock(encryptionPass) storage.isLocked shouldBe false - val addr = Address.fromMnemonic(NetworkType.TESTNET, mnemonic) + val addr = Address.fromMnemonic(NetworkType.TESTNET, mnemonicWithPassword) val secret = storage.getSecret() secret should not be(null) - val expSecret = JavaHelpers.seedToMasterKey(mnemonic.getPhrase, mnemonic.getPassword) + val expSecret = JavaHelpers.seedToMasterKey(mnemonicWithPassword.getPhrase, mnemonicWithPassword.getPassword) expSecret shouldBe secret storage.getAddressFor(NetworkType.TESTNET) shouldBe addr } @@ -32,14 +32,14 @@ class SecretStorageSpec extends PropSpec with Matchers with ScalaCheckDrivenProp property("not unlock by wrong password") { a[RuntimeException] shouldBe thrownBy { - withNewStorageFor(mnemonic, encryptionPass) { storage => + withNewStorageFor(mnemonicWithPassword, encryptionPass) { storage => storage.unlock("wrong password") } } } property("load from file") { - withNewStorageFor(mnemonic, encryptionPass) { storage => + withNewStorageFor(mnemonicWithPassword, encryptionPass) { storage => val fileName = storage.getFile.getPath val loaded = SecretStorage.loadFrom(fileName) loaded.isLocked shouldBe true From 3b60bc590ae6db497d765f6cedee743944ab4c99 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sat, 12 Jun 2021 16:30:52 +0300 Subject: [PATCH 05/10] fix compilation --- .../src/main/java/org/ergoplatform/appkit/JavaHelpers.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala index 4170b618..45a6f444 100644 --- a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala +++ b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala @@ -27,7 +27,7 @@ import java.util.{Map => JMap, List => JList} import sigmastate.utils.Helpers._ // don't remove, required for Scala 2.11 import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix import org.ergoplatform.appkit.Iso.{isoErgoTokenToPair, JListToColl} -import org.ergoplatform.wallet.{TokensMap, Constants} +import org.ergoplatform.wallet.TokensMap import scorex.util.encode.Base16 import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.{ProveDHTuple, DiffieHellmanTupleProverInput} @@ -444,7 +444,7 @@ object JavaHelpers { * The resulting path corresponds to `m/44'/429'/0'/0/index` path. */ def eip3DerivationWithLastIndex(index: Int) = { - val firstPath = Constants.eip3DerivationPath + val firstPath = org.ergoplatform.wallet.Constants.eip3DerivationPath DerivationPath(firstPath.decodedPath.dropRight(1) :+ index, firstPath.publicBranch) } } From fe92988dc1a1eeb770106d727228a9b63a6a9ff1 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sun, 13 Jun 2021 13:33:21 +0300 Subject: [PATCH 06/10] ErgoProverBuilder.withEip3Secret method added --- .../ergoplatform/appkit/TxBuilderSpec.scala | 29 +++++++++++++++++++ .../org/ergoplatform/appkit/ErgoProver.java | 2 ++ .../appkit/ErgoProverBuilder.java | 9 ++++++ .../appkit/impl/ErgoProverBuilderImpl.scala | 17 +++++++++++ 4 files changed, 57 insertions(+) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala index 3ac346e1..df22ec5a 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala @@ -148,4 +148,33 @@ class TxBuilderSpec extends PropSpec with Matchers signed.getOutputsToSpend.size() shouldBe 2 } } + + property("ErgoProverBuilder.withEip3Secret require mnemonic") { + val ergoClient = createMockedErgoClient(MockData(Nil, Nil)) + assertExceptionThrown( + ergoClient.execute { ctx: BlockchainContext => + ctx.newProverBuilder() + .withEip3Secret(0) + .build() + }, + exceptionLike[IllegalArgumentException]( + "Mnemonic is not specified, use withMnemonic method.") + ) + } + + property("ErgoProverBuilder.withEip3Secret check uniqueness of derivation index") { + val ergoClient = createMockedErgoClient(MockData(Nil, Nil)) + assertExceptionThrown( + ergoClient.execute { ctx: BlockchainContext => + ctx.newProverBuilder() + .withMnemonic(mnemonic, SecretString.empty()) + .withEip3Secret(0) + .withEip3Secret(0) // attempt to add the same index + .build() + }, + exceptionLike[IllegalArgumentException]( + "Secret key for derivation index 0 has already been added.") + ) + } + } diff --git a/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java b/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java index 37ed2fa8..264e48f9 100644 --- a/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java +++ b/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java @@ -15,6 +15,8 @@ public interface ErgoProver { /** * Returns Pay-To-Public-Key address of this prover (represented as {@link Address}). + * The returned address corresponds to the master secret derived from the mnemonic + * phrase configured in the builder. */ Address getAddress(); diff --git a/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProverBuilder.java b/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProverBuilder.java index 45f289bd..6121a549 100644 --- a/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProverBuilder.java +++ b/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProverBuilder.java @@ -24,6 +24,15 @@ public interface ErgoProverBuilder { */ ErgoProverBuilder withMnemonic(Mnemonic mnemonic); + /** + * Configure this builder to derive the new EIP-3 secret key with the given index. + * The derivation uses master key derived from the mnemonic configured using {@link + * ErgoProverBuilder#withMnemonic(SecretString, SecretString)}. + * + * @param index last index in the EIP-3 derivation path. + */ + ErgoProverBuilder withEip3Secret(int index); + /** * Configure this builder to use the given {@link SecretStorage} when building a new prover. * diff --git a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala index 201f0d1f..10b88a21 100644 --- a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala +++ b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala @@ -8,8 +8,14 @@ import special.sigma.GroupElement import java.math.BigInteger import java.util +import scala.collection.mutable.ArrayBuffer + class ErgoProverBuilderImpl(_ctx: BlockchainContextImpl) extends ErgoProverBuilder { private var _masterKey: ExtendedSecretKey = _ + + /** Generated EIP-3 secret keys paired with their derivation path index. */ + private var _eip2Keys = ArrayBuffer.empty[(Int, ExtendedSecretKey)] + private val _dhtSecrets = new util.ArrayList[DiffieHellmanTupleProverInput] private val _dLogSecrets = new util.ArrayList[DLogProtocol.DLogProverInput] @@ -22,6 +28,17 @@ class ErgoProverBuilderImpl(_ctx: BlockchainContextImpl) extends ErgoProverBuild override def withMnemonic(mnemonic: Mnemonic): ErgoProverBuilder = withMnemonic(mnemonic.getPhrase, mnemonic.getPassword) + override def withEip3Secret(index: Int): ErgoProverBuilder = { + require(_masterKey != null, s"Mnemonic is not specified, use withMnemonic method.") + require(!_eip2Keys.exists(_._1 == index), + s"Secret key for derivation index $index has already been added.") + val path = JavaHelpers.eip3DerivationWithLastIndex(index) + val secretKey = _masterKey.derive(path) + _eip2Keys += (index -> secretKey) + this + } + + override def withSecretStorage(storage: SecretStorage): ErgoProverBuilder = { if (storage.isLocked) throw new IllegalStateException("SecretStorage is locked, call unlock(password) method") From 589f7aa355a160b8af43552f4c4339e51249a64b Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sun, 13 Jun 2021 14:08:00 +0300 Subject: [PATCH 07/10] ErgoProver.getEip3Addresses method added --- .../ergoplatform/appkit/TxBuilderSpec.scala | 19 +++++++++++++++++++ .../org/ergoplatform/appkit/ErgoProver.java | 9 +++++++++ .../appkit/impl/ErgoProverBuilderImpl.scala | 6 ++++-- .../appkit/impl/ErgoProverImpl.scala | 19 ++++++++++++++++++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala index df22ec5a..1159ba16 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala @@ -177,4 +177,23 @@ class TxBuilderSpec extends PropSpec with Matchers ) } + private def testEip3Address(ctx: BlockchainContext, index: Int): Address = { + Address.createEip3Address(index, ctx.getNetworkType, + mnemonic, SecretString.empty()) + } + + property("ErgoProverBuilder.withEip3Secret should pass secrets to the prover") { + val ergoClient = createMockedErgoClient(MockData(Nil, Nil)) + ergoClient.execute { ctx: BlockchainContext => + val prover = ctx.newProverBuilder() + .withMnemonic(mnemonic, SecretString.empty()) + .withEip3Secret(0) + .withEip3Secret(1) + .build() + assert(prover.getEip3Addresses.contains(testEip3Address(ctx, 0))) + assert(prover.getEip3Addresses.contains(testEip3Address(ctx, 1))) + } + } + + } diff --git a/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java b/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java index 264e48f9..a26c94fc 100644 --- a/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java +++ b/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java @@ -4,6 +4,8 @@ import org.ergoplatform.wallet.protocol.context.ErgoLikeParameters; import special.sigma.BigInt; +import java.util.List; + /** * Interface of the provers that can be used to sign {@link UnsignedTransaction}s. */ @@ -27,6 +29,13 @@ public interface ErgoProver { */ BigInt getSecretKey(); + /** + * Returns a master secret key of this prover. + * + * @see BIP-32 + */ + List
getEip3Addresses(); + /** * Signs unsigned transaction by using configured secrets. * This method delegate to {@link ErgoProver#sign(UnsignedTransaction, int)} with diff --git a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala index 10b88a21..3eb0f02f 100644 --- a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala +++ b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverBuilderImpl.scala @@ -7,7 +7,7 @@ import sigmastate.basics.{DLogProtocol, DiffieHellmanTupleProverInput} import special.sigma.GroupElement import java.math.BigInteger import java.util - +import JavaHelpers._ import scala.collection.mutable.ArrayBuffer class ErgoProverBuilderImpl(_ctx: BlockchainContextImpl) extends ErgoProverBuilder { @@ -83,8 +83,10 @@ class ErgoProverBuilderImpl(_ctx: BlockchainContextImpl) extends ErgoProverBuild override def blockVersion: Byte = _params.getBlockVersion.byteValue } val keys = new util.ArrayList[ExtendedSecretKey] - if (_masterKey != null) + if (_masterKey != null) { keys.add(_masterKey) + keys.addAll(_eip2Keys.map(_._2).to[IndexedSeq].convertTo[util.List[ExtendedSecretKey]]) + } val interpreter = new AppkitProvingInterpreter(keys, _dLogSecrets, _dhtSecrets, parameters) new ErgoProverImpl(_ctx, interpreter) } diff --git a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverImpl.scala b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverImpl.scala index f63cea34..0273633e 100644 --- a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverImpl.scala +++ b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ErgoProverImpl.scala @@ -1,16 +1,22 @@ package org.ergoplatform.appkit.impl +import java.util + import org.ergoplatform.P2PKAddress import org.ergoplatform.appkit._ +import org.ergoplatform.wallet.secrets.ExtendedSecretKey import sigmastate.eval.CostingSigmaDslBuilder import special.sigma.BigInt import sigmastate.utils.Helpers._ // don't remove, required for Scala 2.11 +import JavaHelpers._ class ErgoProverImpl(_ctx: BlockchainContextImpl, _prover: AppkitProvingInterpreter) extends ErgoProver { + private def networkPrefix = _ctx.getNetworkType.networkPrefix + override def getP2PKAddress: P2PKAddress = { val pk = _prover.pubKeys(0) - JavaHelpers.createP2PKAddress(pk, _ctx.getNetworkType.networkPrefix) + JavaHelpers.createP2PKAddress(pk, networkPrefix) } override def getAddress = new Address(getP2PKAddress) @@ -18,6 +24,17 @@ class ErgoProverImpl(_ctx: BlockchainContextImpl, override def getSecretKey: BigInt = CostingSigmaDslBuilder.BigInt(_prover.secretKeys.get(0).privateInput.w) + override def getEip3Addresses: util.List[Address] = { + val addresses = _prover.secretKeys + .convertTo[IndexedSeq[ExtendedSecretKey]] + .drop(1) + .map { k => + val p2pkAddress = JavaHelpers.createP2PKAddress(k.publicImage, networkPrefix) + new Address(p2pkAddress) + } + addresses.convertTo[util.List[Address]] + } + override def sign(tx: UnsignedTransaction): SignedTransaction = sign(tx, baseCost = 0) From c6b616518d4dedf1134dae13ec3109a8ce840b59 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 14 Jun 2021 23:22:19 +0300 Subject: [PATCH 08/10] support EIP-3 addresses in BoxOperations --- .../appkit/AnonymousAccessSpec.scala | 2 +- .../ergoplatform/appkit/TxBuilderSpec.scala | 33 +++++++++++- .../ergoplatform/appkit/BoxOperations.java | 53 ++++++++++++++++--- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/AnonymousAccessSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/AnonymousAccessSpec.scala index 76820662..d981ffae 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/AnonymousAccessSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/AnonymousAccessSpec.scala @@ -105,7 +105,7 @@ object DhtUtils { | proveDHTuple(groupGenerator, g_y, g_x, g_xy) // for alice |}""".stripMargin); - val dhtBoxCreationTx = putToContractTx(ctx, sender, contract, amountToSend) + val dhtBoxCreationTx = putToContractTx(ctx, sender, false, contract, amountToSend) dhtBoxCreationTx } diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala index 1159ba16..94373697 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala @@ -1,5 +1,6 @@ package org.ergoplatform.appkit +import java.io.File import java.math.BigInteger import java.util.Arrays @@ -190,10 +191,38 @@ class TxBuilderSpec extends PropSpec with Matchers .withEip3Secret(0) .withEip3Secret(1) .build() - assert(prover.getEip3Addresses.contains(testEip3Address(ctx, 0))) - assert(prover.getEip3Addresses.contains(testEip3Address(ctx, 1))) + assert(prover.getEip3Addresses.size() == 2) + assert(prover.getEip3Addresses.get(0) == testEip3Address(ctx, 0)) + assert(prover.getEip3Addresses.get(1) == testEip3Address(ctx, 1)) } } + property("send to recipient (non EIP-3)") { + val data = MockData( + Seq( + loadNodeResponse("response_Box1.json"), + loadNodeResponse("response_Box2.json"), + loadNodeResponse("response_Box3.json"), + "21f84cf457802e66fb5930fb5d45fbe955933dc16a72089bf8980797f24e2fa1"), + Seq( + loadExplorerResponse("response_boxesByAddressUnspent.json"))) + + val ergoClient = createMockedErgoClient(data) + + ergoClient.execute(ctx => { + val senderProver = BoxOperations.createProver(ctx, + new File("storage/E2.json").getPath, "abc") + .withEip3Secret(0) + .withEip3Secret(1) + .build + + val recipient = senderProver.getEip3Addresses.get(1) + val amountToSend = 1000000 + val pkContract = ErgoContracts.sendToPK(ctx, recipient) + val signed = BoxOperations.putToContractTx(ctx, + senderProver, false, pkContract, amountToSend) + assert(signed != null) + }) + } } diff --git a/lib-api/src/main/java/org/ergoplatform/appkit/BoxOperations.java b/lib-api/src/main/java/org/ergoplatform/appkit/BoxOperations.java index a6e8d4fa..371fe85e 100644 --- a/lib-api/src/main/java/org/ergoplatform/appkit/BoxOperations.java +++ b/lib-api/src/main/java/org/ergoplatform/appkit/BoxOperations.java @@ -1,9 +1,12 @@ package org.ergoplatform.appkit; +import org.ergoplatform.P2PKAddress; + import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static com.google.common.base.Preconditions.checkState; import static org.ergoplatform.appkit.Parameters.MinFee; /** @@ -45,11 +48,26 @@ public static ErgoProverBuilder createProver( return proverB; } + /** + * Send the given `amountToSend` to the recipient from either the MASTER address of the + * given prover or from the EIP-3 addresses. + * All the derived EIP-3 addresses of the prover can be used to collect unspent boxes. + * + * @param ctx blockchain context obtained from {@link ErgoClient} + * @param senderProver prover which is used to sign transaction + * @param useEip3Addresses true if EIP-3 addresses of the prover should be used to + * withdraw funds (use false for backwards compatibility) + * @param recipient the recipient address + * @param amountToSend amount of NanoErgs to send + */ public static String send( - BlockchainContext ctx, ErgoProver senderProver, Address recipient, long amountToSend) { + BlockchainContext ctx, + ErgoProver senderProver, boolean useEip3Addresses, + Address recipient, long amountToSend) { ErgoContract pkContract = ErgoContracts.sendToPK(ctx, recipient); - SignedTransaction signed = putToContractTx(ctx, senderProver, pkContract, amountToSend); + SignedTransaction signed = putToContractTx(ctx, senderProver, useEip3Addresses, + pkContract, amountToSend); ctx.sendTransaction(signed); return signed.toJson(true); } @@ -60,11 +78,32 @@ public static List loadTop(BlockchainContext ctx, Address sender, long return selected; } - public static SignedTransaction putToContractTx( - BlockchainContext ctx, ErgoProver senderProver, ErgoContract contract, long amountToSend) { - Address sender = senderProver.getAddress(); - List boxesToSpend = loadTop(ctx, sender, amountToSend + MinFee); + public static List loadTop(BlockchainContext ctx, List
senderAddresses, long amount) { + List unspentBoxes = new ArrayList<>(); + for (Address sender : senderAddresses) { + List unspent = ctx.getUnspentBoxesFor(sender); + unspentBoxes.addAll(unspent); + } + List selected = selectTop(unspentBoxes, amount); + return selected; + } + public static SignedTransaction putToContractTx( + BlockchainContext ctx, + ErgoProver senderProver, boolean useEip3Addresses, + ErgoContract contract, long amountToSend) { + List
senders = new ArrayList<>(); + if (useEip3Addresses) { + List
eip3Addresses = senderProver.getEip3Addresses(); + checkState(eip3Addresses.size() > 0, + "EIP-3 addresses are not derived in the prover (use ErgoProverBuilder.withEip3Secret)"); + senders.addAll(eip3Addresses); + } else { + senders.add(senderProver.getAddress()); + } + List boxesToSpend = loadTop(ctx, senders, amountToSend + MinFee); + + P2PKAddress changeAddress = (useEip3Addresses) ? senderProver.getEip3Addresses().get(0).asP2PK() : senderProver.getP2PKAddress(); UnsignedTransactionBuilder txB = ctx.newTxBuilder(); OutBox newBox = txB.outBoxBuilder() .value(amountToSend) @@ -73,7 +112,7 @@ public static SignedTransaction putToContractTx( UnsignedTransaction tx = txB.boxesToSpend(boxesToSpend) .outputs(newBox) .fee(Parameters.MinFee) - .sendChangeTo(senderProver.getP2PKAddress()) + .sendChangeTo(changeAddress) .build(); SignedTransaction signed = senderProver.sign(tx); From 65297c20bc112c0bb9fe7ad332d92db64d0ddcee Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 15 Jun 2021 00:55:24 +0300 Subject: [PATCH 09/10] fix Scala 2.11 --- .../test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala index 94373697..6ebd328b 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala @@ -209,7 +209,7 @@ class TxBuilderSpec extends PropSpec with Matchers val ergoClient = createMockedErgoClient(data) - ergoClient.execute(ctx => { + ergoClient.execute { ctx: BlockchainContext => val senderProver = BoxOperations.createProver(ctx, new File("storage/E2.json").getPath, "abc") .withEip3Secret(0) @@ -222,7 +222,7 @@ class TxBuilderSpec extends PropSpec with Matchers val signed = BoxOperations.putToContractTx(ctx, senderProver, false, pkContract, amountToSend) assert(signed != null) - }) + } } } From 6726112f5f6fc670e85a712a57b80934029443fa Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 15 Jun 2021 13:41:29 +0300 Subject: [PATCH 10/10] ergo-wallet upgraded to v4.0.12 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index d49e28c5..f371d452 100644 --- a/build.sbt +++ b/build.sbt @@ -123,7 +123,7 @@ assemblyMergeStrategy in assembly := { lazy val allConfigDependency = "compile->compile;test->test" val sigmaStateVersion = "4.0.3" -val ergoWalletVersion = "4.0.11" +val ergoWalletVersion = "4.0.12" lazy val sigmaState = ("org.scorexfoundation" %% "sigma-state" % sigmaStateVersion).force() .exclude("ch.qos.logback", "logback-classic") .exclude("org.scorexfoundation", "scrypto")