From b8af768dd9682d7cedb9d47b292d702e465faa04 Mon Sep 17 00:00:00 2001 From: Benjamin Schulte Date: Tue, 5 Apr 2022 15:11:36 +0200 Subject: [PATCH 1/6] ErgoAuthUtils added for EIP-28 --- .../ergoplatform/appkit/ColdErgoClient.scala | 15 ++++- .../ergoplatform/appkit/ErgoAuthSpec.scala | 41 ++++++++++++ .../ergoplatform/appkit/TxBuilderSpec.scala | 9 +-- .../ergoplatform/appkit/ErgoAuthUtils.java | 62 +++++++++++++++++++ .../org/ergoplatform/appkit/JavaHelpers.scala | 16 ++++- .../org/ergoplatform/appkit/Parameters.java | 5 ++ .../appkit/impl/ColdBlockchainContext.scala | 11 ++-- 7 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala create mode 100644 common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java diff --git a/appkit/src/main/scala/org/ergoplatform/appkit/ColdErgoClient.scala b/appkit/src/main/scala/org/ergoplatform/appkit/ColdErgoClient.scala index c7705dd5..36119657 100644 --- a/appkit/src/main/scala/org/ergoplatform/appkit/ColdErgoClient.scala +++ b/appkit/src/main/scala/org/ergoplatform/appkit/ColdErgoClient.scala @@ -2,9 +2,20 @@ package org.ergoplatform.appkit import java.util.function import org.ergoplatform.restapi.client -import org.ergoplatform.appkit.impl.{BlockchainContextBuilderImpl, ColdBlockchainContext} +import org.ergoplatform.appkit.impl.{ColdBlockchainContext, NodeInfoParameters} +import org.ergoplatform.restapi.client.NodeInfo + +class ColdErgoClient(networkType: NetworkType, params: BlockchainParameters) extends ErgoClient { + + /** + * Convenience constructor for giving maxBlockCost + */ + def this(networkType: NetworkType, maxBlockCost: Int) { + this(networkType, new NodeInfoParameters( + new NodeInfo().parameters(new client.Parameters() + .maxBlockCost(Integer.valueOf(maxBlockCost))))) + } -class ColdErgoClient(networkType: NetworkType, params: client.Parameters) extends ErgoClient { override def execute[T](action: function.Function[BlockchainContext, T]): T = { val ctx = new ColdBlockchainContext(networkType, params) val res = action.apply(ctx) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala new file mode 100644 index 00000000..6a5f6ecf --- /dev/null +++ b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala @@ -0,0 +1,41 @@ +package org.ergoplatform.appkit + +import org.scalatest.{Matchers, PropSpec} +import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks +import scorex.util.Random +import sigmastate.interpreter.HintsBag + +import java.nio.charset.StandardCharsets + +class ErgoAuthSpec extends PropSpec with Matchers with ScalaCheckDrivenPropertyChecks + with AppkitTestingCommon { + + property("ErgoAuth address roundtrip") { + // server side + val serializedSigmaBoolean = ErgoAuthUtils.serializeSigmaBoolean(address) + // message to sign should be something random and not repeating + val requestedMessage = addrStr + System.currentTimeMillis().toString + + // transferred to client, and now we are on client side + val signedMessage = new String(Random.randomBytes(16)) + requestedMessage + + new String(Random.randomBytes(32)) + val signature = new ColdErgoClient(address.getNetworkType, Parameters.ColdClientMaxBlockCost) + .execute { ctx => + + val prover = ctx.newProverBuilder().withMnemonic(mnemonic, SecretString.empty()).build() + prover.signMessage(ErgoAuthUtils.deserializeSigmaBoolean(serializedSigmaBoolean), + signedMessage.getBytes(StandardCharsets.UTF_8), + HintsBag.empty) + } + + // transferred to server... + ErgoAuthUtils.verifyResponse(address, requestedMessage, signedMessage, signature) shouldBe (true) + + // and in case someone wanted to fool us + ErgoAuthUtils.verifyResponse(address, + requestedMessage, + signedMessage, + new Array[Byte](0)) shouldBe (false) + } + +} diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala index 918aaaf7..5294b1e0 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala @@ -1,9 +1,8 @@ package org.ergoplatform.appkit import org.ergoplatform.appkit.InputBoxesSelectionException.NotEnoughErgsException -import org.ergoplatform.appkit.impl.{BlockchainContextImpl, Eip4TokenBuilder, ErgoTreeContract} +import org.ergoplatform.appkit.impl.{Eip4TokenBuilder, ErgoTreeContract} import org.ergoplatform.appkit.testing.AppkitTesting -import org.ergoplatform.restapi.client import org.ergoplatform.{ErgoBox, ErgoScriptPredef} import org.scalacheck.Gen import org.scalatest.{Matchers, PropSpec} @@ -290,10 +289,8 @@ class TxBuilderSpec extends PropSpec with Matchers // the only necessary parameter can either be hard-coded or passed // together with ReducedTransaction - val blockchainParams = new client.Parameters() - .maxBlockCost(Integer.valueOf(1000000)) - - val coldClient = new ColdErgoClient(NetworkType.MAINNET, blockchainParams) + val maxBlockCost = Parameters.ColdClientMaxBlockCost + val coldClient = new ColdErgoClient(NetworkType.MAINNET, maxBlockCost) coldClient.execute { ctx: BlockchainContext => // test that context is cold diff --git a/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java b/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java new file mode 100644 index 00000000..55f5f699 --- /dev/null +++ b/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java @@ -0,0 +1,62 @@ +package org.ergoplatform.appkit; + +import java.nio.charset.StandardCharsets; + +import sigmastate.Values.SigmaBoolean; + +/** + * Helper utilities for EIP-28 ErgoAuth + */ +public class ErgoAuthUtils { + + /** + * Serializes the given SigmaBoolean to be sent in ErgoAuthRequest. + */ + public static byte[] serializeSigmaBoolean(SigmaBoolean sigmaBoolean) { + return Iso.isoSigmaBooleanToByteArray().to(sigmaBoolean); + } + + /** + * Converts given address to SigmaBoolean and serializes it to be sent in ErgoAuthRequest. + */ + public static byte[] serializeSigmaBoolean(Address address) { + return serializeSigmaBoolean(address.getPublicKey()); + } + + /** + * Deserializes a SigmaBoolean from byte array + */ + public static SigmaBoolean deserializeSigmaBoolean(byte[] serializedSigmaBoolean) { + return Iso.isoSigmaBooleanToByteArray().from(serializedSigmaBoolean); + } + + /** + * Verifies an ErgoAuthResponse + * + * @param sigmaBoolean the SigmaBoolean needed to be fulfilled for signing the message + * @param originalMessage the original message sent in ErgoAuthRequest, needs to be contained + * in signedMessage + * @param signedMessage the message signed by client + * @param signature signature for signedMessage + * @return whether verification is successful + */ + public static boolean verifyResponse(SigmaBoolean sigmaBoolean, String originalMessage, + String signedMessage, byte[] signature) { + + if (!signedMessage.contains(originalMessage)) + return false; + + return Signature.verifySignature(sigmaBoolean, + signedMessage.getBytes(StandardCharsets.UTF_8), + signature); + } + + /** + * Verifies an ErgoAuthResponnse for a given address. + * See {@link #verifyResponse(SigmaBoolean, String, String, byte[])} for more information. + */ + public static boolean verifyResponse(Address address, String originalMessage, + String signedMessage, byte[] signature) { + return verifyResponse(address.getPublicKey(), originalMessage, signedMessage, signature); + } +} diff --git a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala index 2941be57..d1292cc5 100644 --- a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala +++ b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala @@ -9,8 +9,8 @@ import scala.collection.{mutable, JavaConversions} import org.ergoplatform._ import org.ergoplatform.ErgoBox.TokenId import sigmastate.SType -import sigmastate.Values.{ErgoTree, Constant, SValue, EvaluatedValue} -import sigmastate.serialization.{ValueSerializer, ErgoTreeSerializer, SigmaSerializer, GroupElementSerializer} +import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, SValue, SigmaBoolean} +import sigmastate.serialization.{ErgoTreeSerializer, GroupElementSerializer, SigmaSerializer, ValueSerializer} import scorex.crypto.authds.ADKey import scorex.crypto.hash.Digest32 import org.ergoplatform.wallet.mnemonic.{Mnemonic => WMnemonic} @@ -181,6 +181,18 @@ object Iso extends LowPriorityIsos { JListToColl(isoErgoTokenToPair, RType[(TokenId, Long)]) } + val isoSigmaBooleanToByteArray: Iso[SigmaBoolean, Array[Byte]] = new Iso[SigmaBoolean, Array[Byte]] { + override def to(a: SigmaBoolean): Array[Byte] = { + val w = SigmaSerializer.startWriter() + SigmaBoolean.serializer.serialize(a, w) + w.toBytes + } + override def from(b: Array[Byte]): SigmaBoolean ={ + val r = SigmaSerializer.startReader(b, 0) + SigmaBoolean.serializer.parse(r) + } + } + implicit val jstringToOptionString: Iso[JString, Option[String]] = new Iso[JString, Option[String]] { override def to(a: JString): Option[String] = if (Strings.isNullOrEmpty(a)) None else Some(a) override def from(b: Option[String]): JString = if (b.isEmpty) "" else b.get diff --git a/common/src/main/java/org/ergoplatform/appkit/Parameters.java b/common/src/main/java/org/ergoplatform/appkit/Parameters.java index 51771e20..27a193c2 100644 --- a/common/src/main/java/org/ergoplatform/appkit/Parameters.java +++ b/common/src/main/java/org/ergoplatform/appkit/Parameters.java @@ -27,4 +27,9 @@ public class Parameters { * and `change` output in not added to the transaction. */ public static final long MinChangeValue = 1000 * 1000; + + /** + * Max block cost for Cold Client + */ + public static final int ColdClientMaxBlockCost = 1000000; } diff --git a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ColdBlockchainContext.scala b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ColdBlockchainContext.scala index bf317e99..5c3e13f4 100644 --- a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ColdBlockchainContext.scala +++ b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/ColdBlockchainContext.scala @@ -1,16 +1,13 @@ package org.ergoplatform.appkit.impl +import org.ergoplatform.appkit._ + import java.util -import org.ergoplatform.appkit.{Address, BlockchainDataSource, BlockchainParameters, CoveringBoxes, ErgoProverBuilder, ErgoToken, InputBox, NetworkType, PreHeaderBuilder, SignedTransaction, UnsignedTransactionBuilder} -import org.ergoplatform.restapi.client.{NodeInfo, Parameters} -class ColdBlockchainContext(networkType: NetworkType, params: Parameters) extends BlockchainContextBase(networkType) { +class ColdBlockchainContext(networkType: NetworkType, params: BlockchainParameters) extends BlockchainContextBase(networkType) { override def getDataSource: BlockchainDataSource = throw new UnsupportedOperationException("Cold blockchain context has no data source.") - private val _params = new NodeInfoParameters(new NodeInfo() - .parameters(params)) - - override def getParameters: BlockchainParameters = _params + override def getParameters: BlockchainParameters = params override def createPreHeader(): PreHeaderBuilder = throw new UnsupportedOperationException("Cold blockchain context has no pre header builder.") From dd49dfd66fdd071c75493772e4120d4810e5db6d Mon Sep 17 00:00:00 2001 From: Benjamin Schulte Date: Tue, 5 Apr 2022 15:36:57 +0200 Subject: [PATCH 2/6] ErgoAuthUtils added for EIP-28, fix Scala 2.11 --- .../src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala index 6a5f6ecf..97038e1a 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala @@ -20,7 +20,7 @@ class ErgoAuthSpec extends PropSpec with Matchers with ScalaCheckDrivenPropertyC val signedMessage = new String(Random.randomBytes(16)) + requestedMessage + new String(Random.randomBytes(32)) val signature = new ColdErgoClient(address.getNetworkType, Parameters.ColdClientMaxBlockCost) - .execute { ctx => + .execute { ctx: BlockchainContext => val prover = ctx.newProverBuilder().withMnemonic(mnemonic, SecretString.empty()).build() prover.signMessage(ErgoAuthUtils.deserializeSigmaBoolean(serializedSigmaBoolean), From efaa2fac92fcb5ae784fda5adbd06f2fbe3affa5 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 6 Apr 2022 14:49:00 +0200 Subject: [PATCH 3/6] ergoauth: some cleanups --- common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java | 2 +- .../main/java/org/ergoplatform/appkit/BlockchainParameters.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java b/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java index 55f5f699..5f55523a 100644 --- a/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java +++ b/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java @@ -52,7 +52,7 @@ public static boolean verifyResponse(SigmaBoolean sigmaBoolean, String originalM } /** - * Verifies an ErgoAuthResponnse for a given address. + * Verifies an ErgoAuthResponse for a given address. * See {@link #verifyResponse(SigmaBoolean, String, String, byte[])} for more information. */ public static boolean verifyResponse(Address address, String originalMessage, diff --git a/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainParameters.java b/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainParameters.java index a32a4b06..376505d0 100644 --- a/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainParameters.java +++ b/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainParameters.java @@ -1,7 +1,5 @@ package org.ergoplatform.appkit; -import org.ergoplatform.appkit.NetworkType; - /** * Parameters of the blockchain */ From f7c9bee3a73834a6b13d6a3ff1f8e368812f2990 Mon Sep 17 00:00:00 2001 From: Benjamin Schulte Date: Thu, 7 Apr 2022 10:55:33 +0200 Subject: [PATCH 4/6] Introduced SigmaProp appkit class and used where SigmaBoolean was used before --- .../ergoplatform/appkit/ErgoAuthSpec.scala | 20 ++++++---- .../ergoplatform/appkit/TxBuilderSpec.scala | 9 +++-- .../java/org/ergoplatform/appkit/Address.java | 15 +++++++ .../ergoplatform/appkit/ErgoAuthUtils.java | 38 ++---------------- .../org/ergoplatform/appkit/ErgoType.java | 1 + .../org/ergoplatform/appkit/ErgoValue.java | 1 + .../org/ergoplatform/appkit/SigmaProp.java | 39 +++++++++++++++++++ .../org/ergoplatform/appkit/Signature.java | 23 ++--------- .../org/ergoplatform/appkit/ErgoProver.java | 17 ++------ .../appkit/impl/ErgoProverImpl.scala | 9 +---- 10 files changed, 85 insertions(+), 87 deletions(-) create mode 100644 common/src/main/java/org/ergoplatform/appkit/SigmaProp.java diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala index 97038e1a..e19778fa 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala @@ -11,31 +11,35 @@ class ErgoAuthSpec extends PropSpec with Matchers with ScalaCheckDrivenPropertyC with AppkitTestingCommon { property("ErgoAuth address roundtrip") { - // server side - val serializedSigmaBoolean = ErgoAuthUtils.serializeSigmaBoolean(address) + val sigmaPropFromAddress = SigmaProp.createFromAddress(address) + // ---- server side ---- + val serializedSigmaBoolean = sigmaPropFromAddress.toBytes // message to sign should be something random and not repeating val requestedMessage = addrStr + System.currentTimeMillis().toString - // transferred to client, and now we are on client side + // ---- transferred to client, and now we are on client side ---- + + // EIP-28: "the wallet app adds some own bytes to the obtained message from ErgoAuthRequest" val signedMessage = new String(Random.randomBytes(16)) + requestedMessage + new String(Random.randomBytes(32)) val signature = new ColdErgoClient(address.getNetworkType, Parameters.ColdClientMaxBlockCost) .execute { ctx: BlockchainContext => val prover = ctx.newProverBuilder().withMnemonic(mnemonic, SecretString.empty()).build() - prover.signMessage(ErgoAuthUtils.deserializeSigmaBoolean(serializedSigmaBoolean), + prover.signMessage(SigmaProp.parseFromBytes(serializedSigmaBoolean), signedMessage.getBytes(StandardCharsets.UTF_8), HintsBag.empty) } - // transferred to server... - ErgoAuthUtils.verifyResponse(address, requestedMessage, signedMessage, signature) shouldBe (true) + // ---- transferred to server... ---- + ErgoAuthUtils.verifyResponse(sigmaPropFromAddress, + requestedMessage, signedMessage, signature) shouldBe true // and in case someone wanted to fool us - ErgoAuthUtils.verifyResponse(address, + ErgoAuthUtils.verifyResponse(sigmaPropFromAddress, requestedMessage, signedMessage, - new Array[Byte](0)) shouldBe (false) + new Array[Byte](0)) shouldBe false } } diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala index 5294b1e0..b2262892 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/TxBuilderSpec.scala @@ -73,16 +73,17 @@ class TxBuilderSpec extends PropSpec with Matchers new File("storage/E1.json").getPath, "abc") .build - val signedMessage = proverA.signMessage(proverA.getP2PKAddress, + val proverASigmaPropFromAddress = SigmaProp.createFromAddress(proverA.getAddress) + val signedMessage = proverA.signMessage(proverASigmaPropFromAddress, msg.getBytes, HintsBag.empty) - Signature.verifySignature(proverA.getP2PKAddress, + Signature.verifySignature(proverASigmaPropFromAddress, msg.getBytes, signedMessage) shouldBe true - Signature.verifySignature(proverA.getP2PKAddress, + Signature.verifySignature(proverASigmaPropFromAddress, msg.getBytes, signedMessage) shouldBe true - Signature.verifySignature(proverB.getP2PKAddress, + Signature.verifySignature(SigmaProp.createFromAddress(proverB.getAddress), msg.getBytes, signedMessage) shouldBe false } } diff --git a/common/src/main/java/org/ergoplatform/appkit/Address.java b/common/src/main/java/org/ergoplatform/appkit/Address.java index 76adf269..d2738020 100644 --- a/common/src/main/java/org/ergoplatform/appkit/Address.java +++ b/common/src/main/java/org/ergoplatform/appkit/Address.java @@ -129,6 +129,21 @@ public ErgoContract toErgoContract() { return new ErgoTreeContract(getErgoAddress().script(), getNetworkType()); } + /** + * @return true if this address is a SigmaBoolean + */ + public boolean isSigmaBoolean() { + return isP2PK(); + } + + /** + * @return SigmaBoolean value of this address. Throws an error if + * {@link #isSigmaBoolean()} is false + */ + public Values.SigmaBoolean getSigmaBoolean() { + return getPublicKey(); + } + /** * Create Ergo Address from base58 string. * diff --git a/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java b/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java index 5f55523a..27005fa6 100644 --- a/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java +++ b/common/src/main/java/org/ergoplatform/appkit/ErgoAuthUtils.java @@ -2,61 +2,29 @@ import java.nio.charset.StandardCharsets; -import sigmastate.Values.SigmaBoolean; - /** * Helper utilities for EIP-28 ErgoAuth */ public class ErgoAuthUtils { - /** - * Serializes the given SigmaBoolean to be sent in ErgoAuthRequest. - */ - public static byte[] serializeSigmaBoolean(SigmaBoolean sigmaBoolean) { - return Iso.isoSigmaBooleanToByteArray().to(sigmaBoolean); - } - - /** - * Converts given address to SigmaBoolean and serializes it to be sent in ErgoAuthRequest. - */ - public static byte[] serializeSigmaBoolean(Address address) { - return serializeSigmaBoolean(address.getPublicKey()); - } - - /** - * Deserializes a SigmaBoolean from byte array - */ - public static SigmaBoolean deserializeSigmaBoolean(byte[] serializedSigmaBoolean) { - return Iso.isoSigmaBooleanToByteArray().from(serializedSigmaBoolean); - } - /** * Verifies an ErgoAuthResponse * - * @param sigmaBoolean the SigmaBoolean needed to be fulfilled for signing the message + * @param sigmaProp the Sigma proposition needed to be fulfilled for signing the message * @param originalMessage the original message sent in ErgoAuthRequest, needs to be contained * in signedMessage * @param signedMessage the message signed by client * @param signature signature for signedMessage * @return whether verification is successful */ - public static boolean verifyResponse(SigmaBoolean sigmaBoolean, String originalMessage, + public static boolean verifyResponse(SigmaProp sigmaProp, String originalMessage, String signedMessage, byte[] signature) { if (!signedMessage.contains(originalMessage)) return false; - return Signature.verifySignature(sigmaBoolean, + return Signature.verifySignature(sigmaProp, signedMessage.getBytes(StandardCharsets.UTF_8), signature); } - - /** - * Verifies an ErgoAuthResponse for a given address. - * See {@link #verifyResponse(SigmaBoolean, String, String, byte[])} for more information. - */ - public static boolean verifyResponse(Address address, String originalMessage, - String signedMessage, byte[] signature) { - return verifyResponse(address.getPublicKey(), originalMessage, signedMessage, signature); - } } diff --git a/common/src/main/java/org/ergoplatform/appkit/ErgoType.java b/common/src/main/java/org/ergoplatform/appkit/ErgoType.java index a31494ba..e3884d2a 100644 --- a/common/src/main/java/org/ergoplatform/appkit/ErgoType.java +++ b/common/src/main/java/org/ergoplatform/appkit/ErgoType.java @@ -5,6 +5,7 @@ import scalan.RType; import special.collection.Coll; import special.sigma.*; +import special.sigma.SigmaProp; import java.util.Objects; diff --git a/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java b/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java index c409d418..161b27ac 100644 --- a/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java +++ b/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java @@ -12,6 +12,7 @@ import sigmastate.serialization.ValueSerializer$; import special.collection.Coll; import special.sigma.*; +import special.sigma.SigmaProp; import java.math.BigInteger; import java.util.Objects; diff --git a/common/src/main/java/org/ergoplatform/appkit/SigmaProp.java b/common/src/main/java/org/ergoplatform/appkit/SigmaProp.java new file mode 100644 index 00000000..65053d42 --- /dev/null +++ b/common/src/main/java/org/ergoplatform/appkit/SigmaProp.java @@ -0,0 +1,39 @@ +package org.ergoplatform.appkit; + +import sigmastate.Values; + +/** + * Proposition which can be proven and verified by sigma protocol. + */ +public class SigmaProp { + private final Values.SigmaBoolean sigmaBoolean; + + public SigmaProp(Values.SigmaBoolean sigmaBoolean) { + this.sigmaBoolean = sigmaBoolean; + } + + public Values.SigmaBoolean getSigmaBoolean() { + return sigmaBoolean; + } + + /** + * Serializes this SigmaProp. + */ + public byte[] toBytes() { + return Iso.isoSigmaBooleanToByteArray().to(sigmaBoolean); + } + + /** + * @return SigmaProp equal to the one that was serialized with {@link #toBytes()} + */ + public static SigmaProp parseFromBytes(byte[] serializedBytes) { + return new SigmaProp(Iso.isoSigmaBooleanToByteArray().from(serializedBytes)); + } + + /** + * @return SigmaProp from Address. Note that only SigmaBoolean addresses can be used + */ + public static SigmaProp createFromAddress(Address address) { + return new SigmaProp(address.getSigmaBoolean()); + } +} diff --git a/common/src/main/java/org/ergoplatform/appkit/Signature.java b/common/src/main/java/org/ergoplatform/appkit/Signature.java index 194d339e..dd28e598 100644 --- a/common/src/main/java/org/ergoplatform/appkit/Signature.java +++ b/common/src/main/java/org/ergoplatform/appkit/Signature.java @@ -1,9 +1,7 @@ package org.ergoplatform.appkit; -import org.ergoplatform.P2PKAddress; import org.ergoplatform.ErgoLikeInterpreter; -import sigmastate.Values.SigmaBoolean; import sigmastate.eval.CompiletimeIRContext; public class Signature { @@ -14,25 +12,12 @@ private Signature() { /** * Verifies a signature on given (arbitrary) message for a given public key. * - * @param sigmaTree public key (represented as a sigma proposition tree) - * @param message message to verify + * @param sigmaProp Sigma proposition the message should be signed with + * @param message message to verify * @param signature signature for the message * @return whether signature is valid or not */ - public static boolean verifySignature(SigmaBoolean sigmaTree, byte[] message, byte[] signature) { - return new ErgoLikeInterpreter((new CompiletimeIRContext())).verifySignature(sigmaTree, message, signature); - } - - /** - * Verifies a signature on given (arbitrary) message - * using an address' public key. - * - * @param addr address whose public key will be used to verify message - * @param message message to verify - * @param signature signature for the message - * @return whether signature is valid or not - */ - public static boolean verifySignature(P2PKAddress addr, byte[] message, byte[] signature) { - return new ErgoLikeInterpreter((new CompiletimeIRContext())).verifySignature(addr.pubkey(), message, signature); + public static boolean verifySignature(SigmaProp sigmaProp, byte[] message, byte[] signature) { + return new ErgoLikeInterpreter((new CompiletimeIRContext())).verifySignature(sigmaProp.getSigmaBoolean(), message, signature); } } 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 f7aa5598..63f62769 100644 --- a/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java +++ b/lib-api/src/main/java/org/ergoplatform/appkit/ErgoProver.java @@ -70,24 +70,13 @@ public interface ErgoProver { * Signs an arbitrary message under a key representing a * statement provable via a sigma-protocol. * - * @param sigmaTree public key + * @param sigmaProp Sigma proposition to sign the message with * @param message message to sign * @param hintsBag additional hints for a signer (useful for distributed signing) * @return signed message - * @see Signature#verifySignature(SigmaBoolean, byte[], byte[]) + * @see Signature#verifySignature(SigmaProp, byte[], byte[]) */ - byte[] signMessage(SigmaBoolean sigmaTree, byte[] message, HintsBag hintsBag); - - /** - * Signs an arbitrary message using a p2k address - * - * @param addr address whose public key will be used to sign message - * @param message message to sign - * @param hintsBag additional hints for a signer (useful for distributed signing) - * @return signed message - * @see Signature#verifySignature(P2PKAddress, byte[], byte[]) - */ - byte[] signMessage(P2PKAddress addr, byte[] message, HintsBag hintsBag); + byte[] signMessage(SigmaProp sigmaProp, byte[] message, HintsBag hintsBag); ReducedTransaction reduce(UnsignedTransaction tx, int baseCost); 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 b5d39af8..70c0e284 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 @@ -6,7 +6,6 @@ import org.ergoplatform.P2PKAddress import org.ergoplatform.appkit._ import org.ergoplatform.wallet.secrets.ExtendedSecretKey import sigmastate.eval.CostingSigmaDslBuilder -import sigmastate.Values.{SigmaBoolean} import sigmastate.interpreter.HintsBag import special.sigma.BigInt import sigmastate.utils.Helpers._ // don't remove, required for Scala 2.11 @@ -48,12 +47,8 @@ class ErgoProverImpl(_ctx: BlockchainContextBase, new SignedTransactionImpl(_ctx, signed, cost) } - override def signMessage(sigmaTree: SigmaBoolean, message: Array[Byte], hintsBag: HintsBag): Array[Byte] = { - _prover.signMessage(sigmaTree, message, hintsBag).getOrThrow - } - - override def signMessage(addr: P2PKAddress, message: Array[Byte], hintsBag: HintsBag): Array[Byte] = { - _prover.signMessage(addr.pubkey, message, hintsBag).getOrThrow + override def signMessage(sigmaProp: SigmaProp, message: Array[Byte], hintsBag: HintsBag): Array[Byte] = { + _prover.signMessage(sigmaProp.getSigmaBoolean, message, hintsBag).getOrThrow } override def reduce(tx: UnsignedTransaction, baseCost: Int): ReducedTransaction = { From be587ebb63d98a0a9757cdd611ef126d605d8090 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 8 Apr 2022 18:51:25 +0200 Subject: [PATCH 5/6] ergoauth: introduced SigmaPropInterpreter + code cleanup --- .../appkit/AppkitProvingInterpreter.scala | 19 +++++++++++-------- .../org/ergoplatform/appkit/ErgoType.java | 5 ++--- .../org/ergoplatform/appkit/ErgoValue.java | 3 +-- .../appkit/SigmaPropInterpreter.scala | 15 +++++++++++++++ .../org/ergoplatform/appkit/Signature.java | 6 +----- 5 files changed, 30 insertions(+), 18 deletions(-) create mode 100644 common/src/main/java/org/ergoplatform/appkit/SigmaPropInterpreter.scala diff --git a/common/src/main/java/org/ergoplatform/appkit/AppkitProvingInterpreter.scala b/common/src/main/java/org/ergoplatform/appkit/AppkitProvingInterpreter.scala index 9e737e9e..a7d1f6fe 100644 --- a/common/src/main/java/org/ergoplatform/appkit/AppkitProvingInterpreter.scala +++ b/common/src/main/java/org/ergoplatform/appkit/AppkitProvingInterpreter.scala @@ -3,29 +3,30 @@ package org.ergoplatform.appkit import org.ergoplatform.validation.ValidationRules import org.ergoplatform.wallet.interpreter.ErgoInterpreter import sigmastate.basics.DLogProtocol.{ProveDlog, DLogProverInput} + import java.util import java.util.{List => JList} - import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.wallet.secrets.ExtendedSecretKey -import sigmastate.basics.{SigmaProtocol, SigmaProtocolPrivateInput, SigmaProtocolCommonInput, DiffieHellmanTupleProverInput} +import sigmastate.basics.{SigmaProtocolCommonInput, DiffieHellmanTupleProverInput, SigmaProtocol, SigmaProtocolPrivateInput} import org.ergoplatform._ import org.ergoplatform.utils.ArithUtils -import org.ergoplatform.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext, TransactionContext} +import org.ergoplatform.wallet.protocol.context.{ErgoLikeStateContext, ErgoLikeParameters, TransactionContext} import scorex.crypto.authds.ADKey -import sigmastate.Values.{ErgoTree, SigmaBoolean} +import sigmastate.Values.{SigmaBoolean, ErgoTree} import scala.util.Try -import sigmastate.eval.CompiletimeIRContext -import sigmastate.interpreter.Interpreter.{ReductionResult, ScriptEnv} -import sigmastate.interpreter.{Interpreter, CostedProverResult, ContextExtension, ProverInterpreter, HintsBag} +import sigmastate.eval.{IRContext, CompiletimeIRContext} +import sigmastate.interpreter.Interpreter.{ReductionResult, ScriptEnv, error} +import sigmastate.interpreter.{Interpreter, CostedProverResult, InterpreterContext, PrecompiledScriptProcessor, ContextExtension, ProverInterpreter, HintsBag} import sigmastate.lang.exceptions.CostLimitException import sigmastate.serialization.SigmaSerializer import sigmastate.utxo.CostTable import sigmastate.utils.Helpers._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigmastate.utils.{SigmaByteWriter, SigmaByteReader} import spire.syntax.all.cfor import scalan.util.Extensions.LongOps + import scala.collection.mutable object Helpers { @@ -34,6 +35,8 @@ object Helpers { } } + + /** * A class which holds secrets and can sign transactions (aka generate proofs). * diff --git a/common/src/main/java/org/ergoplatform/appkit/ErgoType.java b/common/src/main/java/org/ergoplatform/appkit/ErgoType.java index e3884d2a..385ee411 100644 --- a/common/src/main/java/org/ergoplatform/appkit/ErgoType.java +++ b/common/src/main/java/org/ergoplatform/appkit/ErgoType.java @@ -5,7 +5,6 @@ import scalan.RType; import special.collection.Coll; import special.sigma.*; -import special.sigma.SigmaProp; import java.util.Objects; @@ -21,7 +20,7 @@ public class ErgoType { private static ErgoType _boolean = new ErgoType(RType.BooleanType()); private static ErgoType _bigInt = new ErgoType(JavaHelpers.BigIntRType()); private static ErgoType _groupElement = new ErgoType(JavaHelpers.GroupElementRType()); - private static ErgoType _sigmaProp = new ErgoType(JavaHelpers.SigmaPropRType()); + private static ErgoType _sigmaProp = new ErgoType(JavaHelpers.SigmaPropRType()); private static ErgoType _avlTree = new ErgoType(JavaHelpers.AvlTreeRType()); private static ErgoType _box = new ErgoType(JavaHelpers.BoxRType()); private static ErgoType
_header = new ErgoType(JavaHelpers.HeaderRType()); @@ -64,7 +63,7 @@ public boolean equals(Object obj) { static public ErgoType groupElementType() { return _groupElement; } - static public ErgoType sigmaPropType() { return _sigmaProp; } + static public ErgoType sigmaPropType() { return _sigmaProp; } static public ErgoType avlTreeType() { return _avlTree; } diff --git a/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java b/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java index 161b27ac..82c56676 100644 --- a/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java +++ b/common/src/main/java/org/ergoplatform/appkit/ErgoValue.java @@ -12,7 +12,6 @@ import sigmastate.serialization.ValueSerializer$; import special.collection.Coll; import special.sigma.*; -import special.sigma.SigmaProp; import java.math.BigInteger; import java.util.Objects; @@ -98,7 +97,7 @@ static public ErgoValue of(GroupElement ge) { return new ErgoValue<>(ge, ErgoType.groupElementType()); } - static public ErgoValue of(Values.SigmaBoolean value) { + static public ErgoValue of(Values.SigmaBoolean value) { return new ErgoValue<>(JavaHelpers.SigmaDsl().SigmaProp(value), ErgoType.sigmaPropType()); } diff --git a/common/src/main/java/org/ergoplatform/appkit/SigmaPropInterpreter.scala b/common/src/main/java/org/ergoplatform/appkit/SigmaPropInterpreter.scala new file mode 100644 index 00000000..f916f0ca --- /dev/null +++ b/common/src/main/java/org/ergoplatform/appkit/SigmaPropInterpreter.scala @@ -0,0 +1,15 @@ +package org.ergoplatform.appkit + +import sigmastate.eval.IRContext +import sigmastate.interpreter.Interpreter.error +import sigmastate.interpreter.{PrecompiledScriptProcessor, Interpreter, InterpreterContext} + +/** Simple light-weight interpreter that don't require IRContext and hence cannot perform + * script reduction, but it can however verify sigma-protocol propositions [[SigmaProp]]. + */ +object SigmaPropInterpreter extends Interpreter { + override type CTX = InterpreterContext + override val IR: IRContext = null + override def precompiledScriptProcessor: PrecompiledScriptProcessor = + error("Script reduction is not supported") +} diff --git a/common/src/main/java/org/ergoplatform/appkit/Signature.java b/common/src/main/java/org/ergoplatform/appkit/Signature.java index dd28e598..6e4c8099 100644 --- a/common/src/main/java/org/ergoplatform/appkit/Signature.java +++ b/common/src/main/java/org/ergoplatform/appkit/Signature.java @@ -1,9 +1,5 @@ package org.ergoplatform.appkit; -import org.ergoplatform.ErgoLikeInterpreter; - -import sigmastate.eval.CompiletimeIRContext; - public class Signature { private Signature() { // prevent instantiation @@ -18,6 +14,6 @@ private Signature() { * @return whether signature is valid or not */ public static boolean verifySignature(SigmaProp sigmaProp, byte[] message, byte[] signature) { - return new ErgoLikeInterpreter((new CompiletimeIRContext())).verifySignature(sigmaProp.getSigmaBoolean(), message, signature); + return SigmaPropInterpreter.verifySignature(sigmaProp.getSigmaBoolean(), message, signature); } } From f1c4855757ba64505f0b0b8f65229b485a569e2b Mon Sep 17 00:00:00 2001 From: Benjamin Schulte Date: Sat, 9 Apr 2022 10:43:59 +0200 Subject: [PATCH 6/6] Address.getSigmaBoolean improved --- .../scala/org/ergoplatform/appkit/ErgoAuthSpec.scala | 2 +- .../main/java/org/ergoplatform/appkit/Address.java | 11 +++++++++-- .../java/org/ergoplatform/appkit/JavaHelpers.scala | 9 ++++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala index e19778fa..8aafcdaf 100644 --- a/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala +++ b/appkit/src/test/scala/org/ergoplatform/appkit/ErgoAuthSpec.scala @@ -10,7 +10,7 @@ import java.nio.charset.StandardCharsets class ErgoAuthSpec extends PropSpec with Matchers with ScalaCheckDrivenPropertyChecks with AppkitTestingCommon { - property("ErgoAuth address roundtrip") { + property("ErgoAuth address round trip") { val sigmaPropFromAddress = SigmaProp.createFromAddress(address) // ---- server side ---- val serializedSigmaBoolean = sigmaPropFromAddress.toBytes diff --git a/common/src/main/java/org/ergoplatform/appkit/Address.java b/common/src/main/java/org/ergoplatform/appkit/Address.java index d2738020..af4524d5 100644 --- a/common/src/main/java/org/ergoplatform/appkit/Address.java +++ b/common/src/main/java/org/ergoplatform/appkit/Address.java @@ -10,6 +10,7 @@ import org.ergoplatform.wallet.secrets.ExtendedPublicKey; import org.ergoplatform.wallet.secrets.ExtendedSecretKey; +import scala.MatchError; import scala.util.Try; import scorex.util.encode.Base58; import sigmastate.Values; @@ -133,7 +134,12 @@ public ErgoContract toErgoContract() { * @return true if this address is a SigmaBoolean */ public boolean isSigmaBoolean() { - return isP2PK(); + try { + getSigmaBoolean(); + return true; + } catch (MatchError me) { + return false; + } } /** @@ -141,7 +147,8 @@ public boolean isSigmaBoolean() { * {@link #isSigmaBoolean()} is false */ public Values.SigmaBoolean getSigmaBoolean() { - return getPublicKey(); + Values.ErgoTree ergoTree = getErgoAddress().script(); + return JavaHelpers$.MODULE$.toSigmaBoolean(ergoTree); } /** diff --git a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala index d1292cc5..6a41e060 100644 --- a/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala +++ b/common/src/main/java/org/ergoplatform/appkit/JavaHelpers.scala @@ -9,7 +9,7 @@ import scala.collection.{mutable, JavaConversions} import org.ergoplatform._ import org.ergoplatform.ErgoBox.TokenId import sigmastate.SType -import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, SValue, SigmaBoolean} +import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, SValue, SigmaBoolean, SigmaPropConstant} import sigmastate.serialization.{ErgoTreeSerializer, GroupElementSerializer, SigmaSerializer, ValueSerializer} import scorex.crypto.authds.ADKey import scorex.crypto.hash.Digest32 @@ -321,6 +321,13 @@ object JavaHelpers { CPreHeader(h.version, h.parentId, h.timestamp, h.nBits, h.height, h.minerPk, h.votes) } + def toSigmaBoolean(ergoTree: ErgoTree): SigmaBoolean = { + val prop = ergoTree.toProposition(ergoTree.isConstantSegregation) + prop match { + case SigmaPropConstant(p) => SigmaDsl.toSigmaBoolean(p) + } + } + def getStateDigest(tree: AvlTree): Array[Byte] = { tree.digest.toArray }