diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala index e2b0fe8f5a..7b77b96f64 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala @@ -80,7 +80,7 @@ trait ProverUtils extends Interpreter { realSecretsToExtract: Seq[SigmaBoolean], simulatedSecretsToExtract: Seq[SigmaBoolean] = Seq.empty): HintsBag = { val reduced = fullReduction(ergoTree, context, Interpreter.emptyEnv) - bagForMultisig(context, reduced.value, proof, realSecretsToExtract, simulatedSecretsToExtract) + bagForMultisig(reduced.value, proof, realSecretsToExtract, simulatedSecretsToExtract) } /** @@ -89,15 +89,13 @@ trait ProverUtils extends Interpreter { * * See DistributedSigSpecification for examples of usage. * - * @param context - context used to reduce the proposition * @param sigmaTree - public key (in form of a sigma-tree) * @param proof - signature for the key * @param realSecretsToExtract - public keys of secrets with real proofs * @param simulatedSecretsToExtract - public keys of secrets with simulated proofs * @return - bag of OtherSecretProven and OtherCommitment hints */ - def bagForMultisig(context: CTX, - sigmaTree: SigmaBoolean, + def bagForMultisig(sigmaTree: SigmaBoolean, proof: Array[Byte], realSecretsToExtract: Seq[SigmaBoolean], simulatedSecretsToExtract: Seq[SigmaBoolean]): HintsBag = { diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala index 43a788bdd3..6baba1e3b0 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala @@ -3,6 +3,7 @@ package org.ergoplatform.sdk import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix import org.ergoplatform._ import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext +import sigmastate.SigmaLeaf import sigmastate.Values.SigmaBoolean import sigmastate.eval.{CostingSigmaDslBuilder, SigmaDsl} import sigmastate.interpreter.HintsBag @@ -91,6 +92,21 @@ class SigmaProver(private[sdk] val _prover: AppkitProvingInterpreter, networkPre _prover.generateCommitments(sigmaTree) } + def extractHints( + proposition: SigmaBoolean, + proof: Array[Byte], + realSecretsToExtract: Seq[SigmaLeaf], + simulatedSecretsToExtract: Seq[SigmaLeaf]): HintsBag = { + _prover.bagForMultisig(proposition, proof, realSecretsToExtract, simulatedSecretsToExtract) + } + + def generateProof( + sb: SigmaBoolean, + messageToSign: Array[Byte], + hintsBag: HintsBag): Array[Byte] = { + _prover.generateProof(sb, messageToSign, hintsBag) + } + override def equals(obj: Any): Boolean = obj match { case that: SigmaProver => if (!this.hasSecrets || !that.hasSecrets) this eq that diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala index ac9e9fd46c..4aef0caa64 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala @@ -53,6 +53,9 @@ case class ReducedTransaction(ergoTx: ReducedErgoLikeTransaction) { * The proofs can be generated by a single prover or by a group of co-singing provers. */ lazy val inputPropositions: Seq[SigmaBoolean] = ergoTx.reducedInputs.map(_.reductionResult.value) + + /** @return transaction bytes to be signed by a prover (so called "message to sign"). */ + def bytesToSign: Array[Byte] = ergoTx.unsignedTx.messageToSign } /** Represents results for transaction signing by a prover like [[SigmaProver]]. */ diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/CosigningServer.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/CosigningServer.scala index 0f56fb2ecf..1d8154d7ea 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/CosigningServer.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/CosigningServer.scala @@ -1,5 +1,7 @@ package org.ergoplatform.sdk.multisig +import sigmastate.SigmaLeaf + import scala.collection.mutable class CosigningServer { @@ -15,11 +17,11 @@ class CosigningServer { sessions.get(id) } - def getSessionsFor(signer: Signer): Seq[SigningSession] = { - val signerKeys = signer.allKeys.toSet + def getSessionsFor(signerKeys: Seq[SigmaLeaf]): Seq[SigningSession] = { + val keyset = signerKeys.toSet sessions.values.filter { s => s.positionsToProve.exists { positionedLeafs => - positionedLeafs.exists(pl => signerKeys.contains(pl.leaf)) + positionedLeafs.exists(pl => keyset.contains(pl.leaf)) } }.toSeq } diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/Signer.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/Signer.scala index e47aee1d00..e654873696 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/Signer.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/Signer.scala @@ -18,6 +18,9 @@ class Signer(val prover: SigmaProver) { /** Mapping from (session id, input proposition) stored hints. */ private val sessions = mutable.HashMap.empty[(SessionId, SigmaBoolean), HintsBag] + /** Mapping from (session id, input proposition) stored proof hints. */ + private val proofs = mutable.HashMap.empty[(SessionId, SigmaBoolean), HintsBag] + def masterAddress: P2PKAddress = prover.getP2PKAddress def pubkey: SigmaLeaf = masterAddress.pubkey @@ -43,6 +46,13 @@ class Signer(val prover: SigmaProver) { bag.realCommitments } + private def generateProof( + sb: SigmaBoolean, sessionId: SessionId, + messageToSign: Array[Byte], + hintsBag: HintsBag): Array[Byte] = { + prover.generateProof(sb, messageToSign, hintsBag) + } + def getActionsFrom(session: SigningSession): Seq[SigningAction] = { val canProveInputs = session.positionsToProve.map { positions => positions.filter { pl => canProve(pl.leaf) } @@ -59,24 +69,22 @@ class Signer(val prover: SigmaProver) { } def execute(action: SigningAction, session: SigningSession): SigningSession = { + val proposition = session.reduced.inputPropositions(action.inputIndex) val newHints = action match { case CreateCommitment(_, inputIndex, pl) => - val proposition = session.reduced.inputPropositions(inputIndex) val commitments = generateCommitments(proposition, session.id) - commitments.filter { c => c.position == pl.position && c.image == pl.leaf } - - // case CreateSignature(signer) => - // val positions = positionsToProve - // val signatures = positions.map { positions => - // positions.map { pl => - // val leaf = pl.leaf - // val position = pl.position - // val signature = signer.prover.createSignature(leaf, position, collectedHints) - // (leaf, position, signature) - // } - // } - // collectedHints.addHints(signatures) - case _ => Seq.empty + commitments.filter(c => c.position == pl.position && c.image == pl.leaf) + + case CreateSignature(_, inputIndex, pl) => + val ownCommitments = getHintsBag(session.id, proposition).get.ownCommitments + val otherCommitments = session.collectedHints(inputIndex) + .filter(_.image != pl.leaf) + val proof = generateProof(proposition, + session.id, + session.reduced.bytesToSign, + otherCommitments.addHints(ownCommitments: _*)) + val proofHints = prover.extractHints(proposition, proof, Seq(pl.leaf), Seq.empty) + proofHints.realProofs.filter(rp => rp.position == pl.position && rp.image == pl.leaf) } session.addHintsAt(action.inputIndex, newHints) } diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/SigningSpec.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/SigningSpec.scala index 4a5baa55e8..d2206d15cf 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/SigningSpec.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/multisig/SigningSpec.scala @@ -126,7 +126,7 @@ class SigningSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matcher val signerPk = signer.pubkey // participants can retrieve related sessions - val session = server.getSessionsFor(signer).head + val session = server.getSessionsFor(signer.allKeys).head session.reduced shouldBe reduced // obtain next actions for the current session state @@ -152,6 +152,19 @@ class SigningSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matcher server.getSession(sessionId).get.collectedHints.size shouldBe cosigners.size + // each cosigner generated a commitment and stores it in the session + cosigners.zipWithIndex.foreach { case (signer, i) => + val signerPk = signer.pubkey + val session = server.getSessionsFor(signer.allKeys).head + val actions = signer.getActionsFrom(session) + + val expectedAction = CreateSignature(signerPk, i, PositionedLeaf.at()(signerPk)) + actions shouldBe Seq(expectedAction) + val newSession = signer.execute(actions.head, session) + + server.updateSession(newSession) + } + } }