diff --git a/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala index b6065ddf01..7a1dbc5c25 100644 --- a/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala +++ b/ergo-core/src/main/scala/org/ergoplatform/nodeView/state/ErgoStateContext.scala @@ -9,12 +9,12 @@ import org.ergoplatform.modifiers.history.header.{Header, HeaderSerializer} import org.ergoplatform.modifiers.history.popow.NipopowAlgos import org.ergoplatform.nodeView.history.ErgoHistoryUtils import org.ergoplatform.nodeView.history.storage.modifierprocessors.ExtensionValidator -import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext import org.ergoplatform.settings.ValidationRules._ import org.ergoplatform.settings._ import org.ergoplatform.utils.ScorexEncoding import org.ergoplatform.serialization.ErgoSerializer import org.ergoplatform.validation.{InvalidModifier, ModifierValidator, ValidationState} +import org.ergoplatform.wallet.interpreter.VersionedBlockchainStateContext import scorex.crypto.authds.ADDigest import scorex.util.ScorexLogging import scorex.util.serialization.{Reader, Writer} @@ -22,6 +22,7 @@ import sigma.Coll import sigma.eval.SigmaDsl import sigma.Extensions.ArrayOps import sigma.crypto.EcPointType +import sigma.validation.SigmaValidationSettings import scala.collection.compat.immutable.ArraySeq import scala.util.{Failure, Success, Try} @@ -68,7 +69,7 @@ class ErgoStateContext(val lastHeaders: Seq[Header], val validationSettings: ErgoValidationSettings, val votingData: VotingData) (implicit val chainSettings: ChainSettings) - extends BlockchainStateContext + extends VersionedBlockchainStateContext with BytesSerializable with ScorexEncoding with ScorexLogging { @@ -78,6 +79,8 @@ class ErgoStateContext(val lastHeaders: Seq[Header], private val votingSettings = chainSettings.voting private val popowAlgos = new NipopowAlgos(chainSettings) + override val sigmaValidationSettings: SigmaValidationSettings = validationSettings.sigmaSettings + override def sigmaPreHeader: sigma.PreHeader = PreHeader.toSigma(lastHeaders.headOption.getOrElse(PreHeader.fake)) diff --git a/ergo-core/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala b/ergo-core/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala index 669579b45c..97f4f7a9a2 100644 --- a/ergo-core/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala +++ b/ergo-core/src/test/scala/org/ergoplatform/settings/VotingSpecification.scala @@ -5,10 +5,9 @@ import org.ergoplatform.modifiers.history.header.Header import org.ergoplatform.nodeView.state.{ErgoStateContext, VotingData} import org.ergoplatform.settings.ValidationRules.rulesSpec import org.ergoplatform.utils.ErgoCorePropertyTest -import sigma.validation.{DisabledRule, ReplacedRule} +import sigma.validation.{DisabledRule, ReplacedRule, ValidationException} import org.ergoplatform.validation.{ValidationRules => VR} import scorex.crypto.authds.ADDigest -import sigmastate.utils.Helpers._ import scala.util.Try @@ -59,6 +58,16 @@ class VotingSpecification extends ErgoCorePropertyTest { } } + property(".toExtensionCandidate && .parseExtension") { + val update = ErgoValidationSettingsUpdate( + Seq.empty, + Seq(1011.toShort -> ReplacedRule(1011), 1008.toShort -> ReplacedRule(1008)) + ) + val vs = ErgoValidationSettings.initial.updated(update) + val vs2 = ErgoValidationSettings.parseExtension(vs.toExtensionCandidate).get + vs2.updateFromInitial == vs.updateFromInitial + } + property("ErgoValidationSettings toExtension/fromExtension roundtrip") { // initial settings should not be written into Extension at all val initial = ErgoValidationSettings.initial @@ -196,6 +205,8 @@ class VotingSpecification extends ErgoCorePropertyTest { val esc12 = process(esc11, expectedParameters12, h12).get checkValidationSettings(esc12.validationSettings, proposedUpdate) + esc12.validationSettings.sigmaSettings.isSoftFork(VR.CheckValidOpCode.id, ValidationException("", VR.CheckValidOpCode, Seq.empty)) shouldBe true + // vote for soft-fork @ activation height val h12w = h12.copy(votes = forkVote) process(esc11, expectedParameters12, h12w).isFailure shouldBe true diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala index c14982c1b6..4217b96130 100644 --- a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/ErgoProvingInterpreter.scala @@ -100,7 +100,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], def signInputs(unsignedTx: UnsignedErgoLikeTransaction, boxesToSpend: IndexedSeq[ErgoBox], dataBoxes: IndexedSeq[ErgoBox], - stateContext: BlockchainStateContext, + stateContext: VersionedBlockchainStateContext, txHints: TransactionHintsBag): Try[(IndexedSeq[Input], Long)] = { if (unsignedTx.inputs.length != boxesToSpend.length) { Failure(new Exception("Not enough boxes to spend")) @@ -136,7 +136,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], unsignedTx, boxIdx.toShort, unsignedInput.extension, - ValidationRules.currentSettings, + stateContext.sigmaValidationSettings, params.maxBlockCost, totalCost, activatedScriptVersion @@ -164,7 +164,7 @@ class ErgoProvingInterpreter(val secretKeys: IndexedSeq[SecretKey], def sign(unsignedTx: UnsignedErgoLikeTransaction, boxesToSpend: IndexedSeq[ErgoBox], dataBoxes: IndexedSeq[ErgoBox], - stateContext: BlockchainStateContext, + stateContext: VersionedBlockchainStateContext, txHints: TransactionHintsBag = TransactionHintsBag.empty): Try[ErgoLikeTransaction] = { val signedInputs: Try[(IndexedSeq[Input], Long)] = diff --git a/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/VersionedBlockchainStateContext.scala b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/VersionedBlockchainStateContext.scala new file mode 100644 index 0000000000..32c245bdf5 --- /dev/null +++ b/ergo-wallet/src/main/scala/org/ergoplatform/wallet/interpreter/VersionedBlockchainStateContext.scala @@ -0,0 +1,8 @@ +package org.ergoplatform.wallet.interpreter + +import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext +import sigma.validation.SigmaValidationSettings + +abstract class VersionedBlockchainStateContext extends BlockchainStateContext { + val sigmaValidationSettings: SigmaValidationSettings +} diff --git a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala index 8ce61b7821..459f32f597 100644 --- a/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala +++ b/ergo-wallet/src/test/scala/org/ergoplatform/wallet/interpreter/InterpreterSpecCommon.scala @@ -2,10 +2,12 @@ package org.ergoplatform.wallet.interpreter import org.ergoplatform.sdk.BlockchainParameters import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext +import org.ergoplatform.validation.ValidationRules import scorex.util.encode.Base16 import sigma.Extensions.ArrayOps import sigma.crypto.CryptoConstants import sigma.data.CGroupElement +import sigma.validation.SigmaValidationSettings import sigma.{Coll, Colls, Header, PreHeader} import sigmastate.eval.CPreHeader @@ -36,7 +38,9 @@ trait InterpreterSpecCommon { override def blockVersion: Byte = 1 } - protected val stateContext = new BlockchainStateContext { + protected val stateContext = new VersionedBlockchainStateContext { + + override val sigmaValidationSettings: SigmaValidationSettings = ValidationRules.currentSettings override def sigmaLastHeaders: Coll[Header] = Colls.emptyColl diff --git a/src/main/resources/devnet.conf b/src/main/resources/devnet.conf index 3fa427b9a5..cfaac1105d 100644 --- a/src/main/resources/devnet.conf +++ b/src/main/resources/devnet.conf @@ -14,7 +14,8 @@ ergo { protocolVersion = 4 # 6.0 soft-fork # Network address prefix, currently reserved values are 0 (money chain mainnet) and 16 (money chain testnet) - addressPrefix = 32 + addressPrefix = 16 + # A difficulty the network starts with initialDifficultyHex = "01" @@ -52,7 +53,7 @@ ergo { voting { 120 = 1 - "rulesToDisable" = [1011, 1008] + "rulesToDisable" = [] } } scorex { diff --git a/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala b/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala index 0a64ba2bd6..14ea4014ae 100644 --- a/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala +++ b/src/main/scala/org/ergoplatform/http/api/ScriptApiRoute.scala @@ -9,11 +9,11 @@ import org.ergoplatform._ import org.ergoplatform.http.api.ApiError.BadRequest import org.ergoplatform.http.api.requests.{CryptoResult, ExecuteRequest} import org.ergoplatform.nodeView.ErgoReadersHolder.{GetReaders, Readers} -import org.ergoplatform.nodeView.wallet.ErgoWalletReader import org.ergoplatform.nodeView.wallet.requests.PaymentRequestDecoder import org.ergoplatform.settings.{ErgoSettings, RESTApiSettings} import scorex.core.api.http.ApiResponse import scorex.util.encode.Base16 +import sigma.VersionContext import sigma.ast.{ByteArrayConstant, ErgoTree, SBoolean, SSigmaProp, Value} import sigma.compiler.{CompilerResult, SigmaCompiler} import sigma.compiler.ir.CompiletimeIRContext @@ -21,7 +21,6 @@ import sigma.data.ProveDlog import sigma.serialization.ValueSerializer import sigmastate.interpreter.Interpreter -import scala.concurrent.Future import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} @@ -58,37 +57,48 @@ case class ScriptApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSettings) keys.zipWithIndex.map { case (pk, i) => s"myPubKey_$i" -> pk }.toMap } - private def compileSource(source: String, env: Map[String, Any]): Try[ErgoTree] = { + private def compileSource(source: String, env: Map[String, Any], treeVersion: Int = 0): Try[ErgoTree] = { val compiler = new SigmaCompiler(ergoSettings.chainSettings.addressPrefix) + val ergoTreeHeader = ErgoTree.defaultHeaderWithVersion(treeVersion.toByte) Try(compiler.compile(env, source)(new CompiletimeIRContext)).flatMap { case CompilerResult(_, _, _, script: Value[SSigmaProp.type@unchecked]) if script.tpe == SSigmaProp => - Success(ErgoTree.fromProposition(script)) + Success(ErgoTree.fromProposition(ergoTreeHeader, script)) case CompilerResult(_, _, _, script: Value[SBoolean.type@unchecked]) if script.tpe == SBoolean => - Success(ErgoTree.fromProposition(script.toSigmaProp)) + Success(ErgoTree.fromProposition(ergoTreeHeader, script.toSigmaProp)) case other => Failure(new Exception(s"Source compilation result is of type ${other.buildTree.tpe}, but `SBoolean` expected")) } } - private def withWalletOp[T](op: ErgoWalletReader => Future[T])(toRoute: T => Route): Route = { - onSuccess((readersHolder ? GetReaders).mapTo[Readers].flatMap(r => op(r.w)))(toRoute) + private def withWalletAndStateOp[T](op: (Readers) => T)(toRoute: T => Route): Route = { + onSuccess((readersHolder ? GetReaders).mapTo[Readers].map(r => op(r)))(toRoute) } + // todo: unite p2sAddress and p2shAddress def p2sAddressR: Route = (path("p2sAddress") & post & source) { source => - withWalletOp(_.publicKeys(0, loadMaxKeys)) { addrs => - compileSource(source, keysToEnv(addrs.map(_.pubkey))).map(Pay2SAddress.apply).fold( - e => BadRequest(e.getMessage), - address => ApiResponse(addressResponse(address)) - ) + withWalletAndStateOp(r => (r.w.publicKeys(0, loadMaxKeys), r.s.stateContext)) { case (addrsF, sc) => + onSuccess(addrsF) { addrs => + VersionContext.withVersions((sc.blockVersion - 1).toByte, 0) { + // todo: treeVersion == 1 is used here, revisit, likely 0 should be default for now + compileSource(source, keysToEnv(addrs.map(_.pubkey)), 1).map(Pay2SAddress.apply).fold( + e => BadRequest(e.getMessage), + address => ApiResponse(addressResponse(address)) + ) + } + } } } def p2shAddressR: Route = (path("p2shAddress") & post & source) { source => - withWalletOp(_.publicKeys(0, loadMaxKeys)) { addrs => - compileSource(source, keysToEnv(addrs.map(_.pubkey))).map(Pay2SHAddress.apply).fold( - e => BadRequest(e.getMessage), - address => ApiResponse(addressResponse(address)) - ) + withWalletAndStateOp(r => (r.w.publicKeys(0, loadMaxKeys), r.s.stateContext.blockVersion)) { case (addrsF, bv) => + onSuccess(addrsF) { addrs => + VersionContext.withVersions((bv - 1).toByte, 0) { + compileSource(source, keysToEnv(addrs.map(_.pubkey))).map(Pay2SHAddress.apply).fold( + e => BadRequest(e.getMessage), + address => ApiResponse(addressResponse(address)) + ) + } + } } } diff --git a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala index 3ecc6c3d89..1facd7ac83 100644 --- a/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala +++ b/src/main/scala/org/ergoplatform/mining/CandidateGenerator.scala @@ -32,6 +32,7 @@ import sigma.Extensions.ArrayOps import sigma.crypto.CryptoFacade import sigma.data.{Digest32Coll, ProveDlog} import sigma.interpreter.ProverResult +import sigma.validation.ReplacedRule import sigma.{Coll, Colls} import scala.annotation.tailrec @@ -355,7 +356,11 @@ object CandidateGenerator extends ScorexLogging { ) None } else { - val desiredUpdate = ergoSettings.votingTargets.desiredUpdate + val desiredUpdate = if (stateContext.blockVersion == 3) { + ergoSettings.votingTargets.desiredUpdate.copy(statusUpdates = Seq(1011.toShort -> ReplacedRule(1011), 1008.toShort -> ReplacedRule(1008))) + } else { + ergoSettings.votingTargets.desiredUpdate + } Some( createCandidate( pk, @@ -399,7 +404,7 @@ object CandidateGenerator extends ScorexLogging { val nextHeightCondition = if (ergoSettings.networkType.isMainNet) { nextHeight >= 823297 // mainnet voting start height, first block of epoch #804 } else { - nextHeight >= 1024 + nextHeight >= 256 } // we automatically vote for 5.0 soft-fork in the mainnet if 120 = 0 vote not provided in settings