diff --git a/benchmark/src/test/scala/com/wavesplatform/state/StateSyntheticBenchmark.scala b/benchmark/src/test/scala/com/wavesplatform/state/StateSyntheticBenchmark.scala index 88bba795815..ec4989cb30b 100644 --- a/benchmark/src/test/scala/com/wavesplatform/state/StateSyntheticBenchmark.scala +++ b/benchmark/src/test/scala/com/wavesplatform/state/StateSyntheticBenchmark.scala @@ -67,7 +67,7 @@ object StateSyntheticBenchmark { val textScript = "sigVerify(tx.bodyBytes,tx.proofs[0],tx.senderPublicKey)" val untypedScript = Parser.parseExpr(textScript).get.value - val typedScript = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedScript).explicitGet()._1 + val typedScript = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untypedScript).explicitGet()._1 val setScriptBlock = nextBlock( Seq( diff --git a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AssetsApiGrpcSpec.scala b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AssetsApiGrpcSpec.scala index 3ecbc270e45..abbeee9e08f 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AssetsApiGrpcSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AssetsApiGrpcSpec.scala @@ -3,6 +3,7 @@ package com.wavesplatform.api.grpc.test import com.google.protobuf.ByteString import com.wavesplatform.account.KeyPair import com.wavesplatform.api.grpc.{AssetInfoResponse, AssetsApiGrpcImpl, NFTRequest, NFTResponse} +import com.wavesplatform.block.Block.ProtoBlockVersion import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.features.BlockchainFeatures @@ -55,6 +56,49 @@ class AssetsApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffMatcher } } + "NODE-999. GetNftList limit should work properly" in withDomain( + RideV6.addFeatures(BlockchainFeatures.ReduceNFTFee), + AddrWithBalance.enoughBalances(sender) + ) { d => + val nftIssues = (1 to 5).map(idx => TxHelpers.issue(sender, 1, name = s"nft$idx", reissuable = false)) + val limit = 2 + val afterId = 1 // second element + + d.appendBlock() + val mb1 = d.appendMicroBlock(nftIssues.take(afterId + 1)*) + d.appendMicroBlock(nftIssues.drop(afterId + 1)*) + + // full liquid + d.rocksDBWriter.containsTransaction(nftIssues(afterId)) shouldBe false + d.rocksDBWriter.containsTransaction(nftIssues(afterId + 1)) shouldBe false + check() + + // liquid afterId + d.appendBlock(d.createBlock(ProtoBlockVersion, nftIssues.drop(afterId + 1), Some(mb1))) + d.rocksDBWriter.containsTransaction(nftIssues(afterId)) shouldBe true + d.rocksDBWriter.containsTransaction(nftIssues(afterId + 1)) shouldBe false + check() + + // full solid + d.appendBlock() + d.rocksDBWriter.containsTransaction(nftIssues(afterId)) shouldBe true + d.rocksDBWriter.containsTransaction(nftIssues(afterId + 1)) shouldBe true + check() + + def check() = { + val (observer, result) = createObserver[NFTResponse] + val request = NFTRequest.of( + ByteString.copyFrom(sender.toAddress.bytes), + limit, + afterAssetId = ByteString.copyFrom(nftIssues(afterId).asset.id.arr) + ) + getGrpcApi(d).getNFTList(request, observer) + val response = result.runSyncUnsafe() + response.size shouldBe limit + response.map(_.assetInfo.get.name) shouldBe nftIssues.slice(afterId + 1, afterId + limit + 1).map(_.name.toStringUtf8) + } + } + private def getGrpcApi(d: Domain) = new AssetsApiGrpcImpl(d.assetsApi, d.accountsApi) } diff --git a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/GRPCBroadcastSpec.scala b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/GRPCBroadcastSpec.scala index a47b4105d7e..97f4b4e997d 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/GRPCBroadcastSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/GRPCBroadcastSpec.scala @@ -1,7 +1,7 @@ package com.wavesplatform.api.grpc.test import scala.concurrent.{Await, Future} -import scala.concurrent.duration.Duration +import scala.concurrent.duration.* import com.wavesplatform.account.Address import com.wavesplatform.common.state.ByteStr import com.wavesplatform.test.{FlatSpec, TestTime} @@ -91,7 +91,7 @@ class GRPCBroadcastSpec extends FlatSpec with BeforeAndAfterAll with PathMockFac @throws[StatusException]("on failed broadcast") def assertBroadcast(tx: Transaction): Unit = { - Await.result(grpcTxApi.broadcast(PBTransactions.protobuf(tx)), Duration.Inf) + Await.result(grpcTxApi.broadcast(PBTransactions.protobuf(tx)), 10.seconds) } } } diff --git a/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala b/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala index 34feeb4a0d9..3d828e80253 100644 --- a/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala +++ b/grpc-server/src/test/scala/com/wavesplatform/events/BlockchainUpdatesSpec.scala @@ -420,7 +420,7 @@ class BlockchainUpdatesSpec extends FreeSpec with WithBUDomain with ScalaFutures (1 to blocksCount + 1).foreach(_ => d.appendBlock()) val result = Await - .result(r.getBlockUpdatesRange(GetBlockUpdatesRangeRequest(1, blocksCount)), Duration.Inf) + .result(r.getBlockUpdatesRange(GetBlockUpdatesRangeRequest(1, blocksCount)), 1.minute) .updates .map(_.update.append.map(_.getBlock.vrf.toByteStr).filterNot(_.isEmpty)) @@ -1215,7 +1215,7 @@ class BlockchainUpdatesSpec extends FreeSpec with WithBUDomain with ScalaFutures Await .result( repo.getBlockUpdate(GetBlockUpdateRequest(height)), - Duration.Inf + 1.minute ) .getUpdate .update diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationError.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationError.scala index 08e377eb8f4..243fa22889e 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationError.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationError.scala @@ -1,7 +1,5 @@ package com.wavesplatform.lang.v1.compiler -import java.nio.charset.StandardCharsets - import cats.Show import com.wavesplatform.lang.v1.ContractLimits import com.wavesplatform.lang.v1.compiler.Types.* @@ -9,6 +7,8 @@ import com.wavesplatform.lang.v1.evaluator.ctx.FunctionTypeSignature import com.wavesplatform.lang.v1.parser.Expressions import com.wavesplatform.lang.v1.parser.Expressions.{Declaration, PART} +import java.nio.charset.StandardCharsets + sealed trait CompilationError { def start: Int def end: Int @@ -52,18 +52,11 @@ object CompilationError { s"but ${names.map(n => s"`$n`").mkString(", ")} found" } - final case class UnusedCaseVariables(start: Int, end: Int, names: List[String]) extends CompilationError { - val message = s"Unused case variable(s) ${names.map(n => s"`$n`").mkString(", ")}" - } - final case class AlreadyDefined(start: Int, end: Int, name: String, isFunction: Boolean) extends CompilationError { val message = if (isFunction) s"Value '$name' can't be defined because function with this name is already defined" else s"Value '$name' already defined in the scope" } - final case class NonExistingType(start: Int, end: Int, name: String, existing: List[String]) extends CompilationError { - val message = s"Value '$name' declared as non-existing type, while all possible types are $existing" - } final case class BadFunctionSignatureSameArgNames(start: Int, end: Int, name: String) extends CompilationError { val message = s"Function '$name' declared with duplicating argument names" diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationStepResultDec.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationStepResultDec.scala new file mode 100644 index 00000000000..aa0f6f7537f --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationStepResultDec.scala @@ -0,0 +1,12 @@ +package com.wavesplatform.lang.v1.compiler + +import com.wavesplatform.lang.v1.compiler.Types.FINAL +import com.wavesplatform.lang.v1.parser.Expressions + +case class CompilationStepResultDec( + ctx: CompilerContext, + dec: Terms.DECLARATION, + t: FINAL, + parseNodeExpr: Expressions.Declaration, + errors: Iterable[CompilationError] = Iterable.empty +) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationStepResultExpr.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationStepResultExpr.scala new file mode 100644 index 00000000000..bf8eb9fd0c5 --- /dev/null +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/CompilationStepResultExpr.scala @@ -0,0 +1,12 @@ +package com.wavesplatform.lang.v1.compiler + +import com.wavesplatform.lang.v1.compiler.Types.FINAL +import com.wavesplatform.lang.v1.parser.Expressions + +case class CompilationStepResultExpr( + ctx: CompilerContext, + expr: Terms.EXPR, + t: FINAL, + parseNodeExpr: Expressions.EXPR, + errors: Iterable[CompilationError] = Iterable.empty +) diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ContractCompiler.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ContractCompiler.scala index a8647939e8c..1240d5aae79 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ContractCompiler.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ContractCompiler.scala @@ -13,7 +13,7 @@ import com.wavesplatform.lang.contract.meta.{MetaMapper, V1 as MetaV1, V2 as Met import com.wavesplatform.lang.directives.values.{StdLibVersion, V3, V6} import com.wavesplatform.lang.v1.compiler.CompilationError.{AlreadyDefined, Generic, UnionNotAllowedForCallableArgs, WrongArgumentType} import com.wavesplatform.lang.v1.compiler.CompilerContext.{VariableInfo, vars} -import com.wavesplatform.lang.v1.compiler.ExpressionCompiler.* +import com.wavesplatform.lang.v1.compiler.ContractCompiler.* import com.wavesplatform.lang.v1.compiler.ScriptResultSource.FreeCall import com.wavesplatform.lang.v1.compiler.Terms.EXPR import com.wavesplatform.lang.v1.compiler.Types.{BOOLEAN, BYTESTR, LONG, STRING} @@ -25,16 +25,13 @@ import com.wavesplatform.lang.v1.parser.Expressions.{FUNC, PART, Type} import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset import com.wavesplatform.lang.v1.parser.{Expressions, Parser} import com.wavesplatform.lang.v1.task.imports.* -import com.wavesplatform.lang.v1.{ContractLimits, FunctionHeader, compiler} +import com.wavesplatform.lang.v1.{ContractLimits, FunctionHeader} import scala.annotation.tailrec -object ContractCompiler { - val FreeCallInvocationArg = "i" - +class ContractCompiler(version: StdLibVersion) extends ExpressionCompiler(version) { private def compileAnnotatedFunc( af: Expressions.ANNOTATEDFUNC, - version: StdLibVersion, saveExprContext: Boolean, allowIllFormedStrings: Boolean, source: ScriptResultSource @@ -88,10 +85,10 @@ object ContractCompiler { _.flatMap(_.dic(version).toList).map(nameAndType => (nameAndType._1, VariableInfo(AnyPos, nameAndType._2))) ) .getOrElse(List.empty) - unionInCallableErrs <- checkCallableUnions(af, annotationsWithErr._1.toList.flatten, version) + unionInCallableErrs <- checkCallableUnions(af, annotationsWithErr._1.toList.flatten) compiledBody <- local { modify[Id, CompilerContext, CompilationError](vars.modify(_)(_ ++ annotationBindings)).flatMap(_ => - compiler.ExpressionCompiler.compileFunc(af.f.position, af.f, saveExprContext, annotationBindings.map(_._1), allowIllFormedStrings) + compileFunc(af.f.position, af.f, saveExprContext, annotationBindings.map(_._1), allowIllFormedStrings) ) } annotatedFuncWithErr <- getCompiledAnnotatedFunc(annotationsWithErr, compiledBody._1).handleError() @@ -132,7 +129,6 @@ object ContractCompiler { private def compileContract( parsedDapp: Expressions.DAPP, - version: StdLibVersion, needCompaction: Boolean, removeUnusedCode: Boolean, source: ScriptResultSource, @@ -149,7 +145,7 @@ object ContractCompiler { annFuncArgTypesErr <- validateAnnotatedFuncsArgTypes(parsedDapp).handleError() compiledAnnFuncsWithErr <- parsedDapp.fs .traverse[CompileM, (Option[AnnotatedFunction], List[(String, Types.FINAL)], Expressions.ANNOTATEDFUNC, Iterable[CompilationError])](af => - local(compileAnnotatedFunc(af, version, saveExprContext, allowIllFormedStrings, source)) + local(compileAnnotatedFunc(af, saveExprContext, allowIllFormedStrings, source)) ) annotatedFuncs = compiledAnnFuncsWithErr.filter(_._1.nonEmpty).map(_._1.get) parsedNodeAFuncs = compiledAnnFuncsWithErr.map(_._3) @@ -236,7 +232,7 @@ object ContractCompiler { } yield result } - def handleValid[T](part: PART[T]): CompileM[PART.VALID[T]] = part match { + private def handleValid[T](part: PART[T]): CompileM[PART.VALID[T]] = part match { case x: PART.VALID[T] => x.pure[CompileM] case PART.INVALID(p, message) => raiseError(Generic(p.start, p.end, message)) } @@ -305,13 +301,7 @@ object ContractCompiler { } } - val primitiveCallableTypes: Set[String] = - Set(LONG, BYTESTR, BOOLEAN, STRING).map(_.name) - - val allowedCallableTypesV4: Set[String] = - primitiveCallableTypes + "List[]" - - private def validateDuplicateVarsInContract(contract: Expressions.DAPP): CompileM[Any] = { + private def validateDuplicateVarsInContract(contract: Expressions.DAPP): CompileM[Any] = for { ctx <- get[Id, CompilerContext, CompilationError] annotationVars = contract.fs.flatMap(_.anns.flatMap(_.args)).traverse[CompileM, PART.VALID[String]](handleValid) @@ -339,7 +329,52 @@ object ContractCompiler { } } } yield () + + private def checkCallableUnions( + func: Expressions.ANNOTATEDFUNC, + annotations: List[Annotation], + ): CompileM[Seq[UnionNotAllowedForCallableArgs]] = { + @tailrec + def containsUnion(tpe: Type): Boolean = + tpe match { + case Expressions.Union(types) if types.size > 1 => true + case Expressions.Single(PART.VALID(_, Type.ListTypeName), Some(PART.VALID(_, Expressions.Union(types)))) if types.size > 1 => true + case Expressions.Single( + PART.VALID(_, Type.ListTypeName), + Some(PART.VALID(_, inner @ Expressions.Single(PART.VALID(_, Type.ListTypeName), _))) + ) => + containsUnion(inner) + case _ => false + } + + val isCallable = annotations.exists { + case CallableAnnotation(_) => true + case _ => false + } + + if (version < V6 || !isCallable) { + Seq.empty[UnionNotAllowedForCallableArgs].pure[CompileM] + } else { + func.f.args + .filter { case (_, tpe) => + containsUnion(tpe) + } + .map { case (argName, _) => + UnionNotAllowedForCallableArgs(argName.position.start, argName.position.end) + } + .pure[CompileM] + } } +} + +object ContractCompiler { + val FreeCallInvocationArg = "i" + + val primitiveCallableTypes: Set[String] = + Set(LONG, BYTESTR, BOOLEAN, STRING).map(_.name) + + val allowedCallableTypesV4: Set[String] = + primitiveCallableTypes + "List[]" def apply( c: CompilerContext, @@ -350,7 +385,8 @@ object ContractCompiler { removeUnusedCode: Boolean = false, allowIllFormedStrings: Boolean = false ): Either[String, DApp] = { - compileContract(contract, version, needCompaction, removeUnusedCode, source, allowIllFormedStrings = allowIllFormedStrings) + new ContractCompiler(version) + .compileContract(contract, needCompaction, removeUnusedCode, source, allowIllFormedStrings = allowIllFormedStrings) .run(c) .map( _._2 @@ -375,7 +411,7 @@ object ContractCompiler { val parser = new Parser(version)(offset) parser.parseContract(input) match { case fastparse.Parsed.Success(xs, _) => - ContractCompiler(ctx, xs, version, source, needCompaction, removeUnusedCode, allowIllFormedStrings) match { + apply(ctx, xs, version, source, needCompaction, removeUnusedCode, allowIllFormedStrings) match { case Left(err) => Left(err) case Right(c) => Right(c) } @@ -396,7 +432,8 @@ object ContractCompiler { new Parser(version)(offset) .parseDAPPWithErrorRecovery(input) .flatMap { case (parseResult, removedCharPosOpt) => - compileContract(parseResult, version, needCompaction, removeUnusedCode, ScriptResultSource.CallableFunction, saveExprContext) + new ContractCompiler(version) + .compileContract(parseResult, needCompaction, removeUnusedCode, ScriptResultSource.CallableFunction, saveExprContext) .run(ctx) .map( _._2 @@ -437,41 +474,4 @@ object ContractCompiler { Left(parser.toString(input, f)) } } - - private def checkCallableUnions( - func: Expressions.ANNOTATEDFUNC, - annotations: List[Annotation], - version: StdLibVersion - ): CompileM[Seq[UnionNotAllowedForCallableArgs]] = { - @tailrec - def containsUnion(tpe: Type): Boolean = - tpe match { - case Expressions.Union(types) if types.size > 1 => true - case Expressions.Single(PART.VALID(_, Type.ListTypeName), Some(PART.VALID(_, Expressions.Union(types)))) if types.size > 1 => true - case Expressions.Single( - PART.VALID(_, Type.ListTypeName), - Some(PART.VALID(_, inner @ Expressions.Single(PART.VALID(_, Type.ListTypeName), _))) - ) => - containsUnion(inner) - case _ => false - } - - val isCallable = annotations.exists { - case CallableAnnotation(_) => true - case _ => false - } - - if (version < V6 || !isCallable) { - Seq.empty[UnionNotAllowedForCallableArgs].pure[CompileM] - } else { - func.f.args - .filter { case (_, tpe) => - containsUnion(tpe) - } - .map { case (argName, _) => - UnionNotAllowedForCallableArgs(argName.position.start, argName.position.end) - } - .pure[CompileM] - } - } } diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala index 8a45f5bfee0..9a9ae79997e 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/ExpressionCompiler.scala @@ -4,28 +4,17 @@ import cats.implicits.* import cats.{Id, Show} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.{CommonError, StringOps} -import com.wavesplatform.lang.directives.values.StdLibVersion +import com.wavesplatform.lang.directives.values.{StdLibVersion, V8} import com.wavesplatform.lang.v1.compiler.CompilationError.* import com.wavesplatform.lang.v1.compiler.CompilerContext.* +import com.wavesplatform.lang.v1.compiler.ExpressionCompiler.* import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.* import com.wavesplatform.lang.v1.evaluator.EvaluatorV1.* import com.wavesplatform.lang.v1.evaluator.ctx.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames import com.wavesplatform.lang.v1.parser.BinaryOperation.* -import com.wavesplatform.lang.v1.parser.Expressions.{ - BINARY_OP, - CompositePattern, - ConstsPat, - MATCH_CASE, - ObjPat, - PART, - Pos, - Single, - TuplePat, - Type, - TypedVar -} +import com.wavesplatform.lang.v1.parser.Expressions.{BINARY_OP, CompositePattern, ConstsPat, MATCH_CASE, ObjPat, PART, Pos, Single, TuplePat, Type, TypedVar} import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset import com.wavesplatform.lang.v1.parser.{BinaryOperation, Expressions, Parser} import com.wavesplatform.lang.v1.task.imports.* @@ -34,90 +23,7 @@ import com.wavesplatform.lang.v1.{ContractLimits, FunctionHeader} import java.nio.charset.StandardCharsets import scala.util.Try -object ExpressionCompiler { - case class CompilationStepResultExpr( - ctx: CompilerContext, - expr: Terms.EXPR, - t: FINAL, - parseNodeExpr: Expressions.EXPR, - errors: Iterable[CompilationError] = Iterable.empty - ) - - case class CompilationStepResultDec( - ctx: CompilerContext, - dec: Terms.DECLARATION, - t: FINAL, - parseNodeExpr: Expressions.Declaration, - errors: Iterable[CompilationError] = Iterable.empty - ) - - def compile( - input: String, - offset: LibrariesOffset, - ctx: CompilerContext, - version: StdLibVersion, - allowIllFormedStrings: Boolean = false - ): Either[String, (EXPR, FINAL)] = { - val parser = new Parser(version)(offset) - parser.parseExpr(input) match { - case fastparse.Parsed.Success(xs, _) => ExpressionCompiler(ctx, xs, allowIllFormedStrings) - case f: fastparse.Parsed.Failure => Left(parser.toString(input, f)) - } - } - - def compileBoolean(input: String, offset: LibrariesOffset, ctx: CompilerContext, version: StdLibVersion): Either[String, EXPR] = { - compile(input, offset, ctx, version).flatMap { - case (expr, BOOLEAN) => Right(expr) - case _ => Left("Script should return boolean") - } - } - - def compileUntyped(input: String, offset: LibrariesOffset, ctx: CompilerContext, version: StdLibVersion): Either[String, EXPR] = { - compile(input, offset, ctx, version) - .map { case (expr, _) => expr } - } - - def compileWithParseResult( - input: String, - offset: LibrariesOffset, - ctx: CompilerContext, - version: StdLibVersion, - saveExprContext: Boolean = true - ): Either[(String, Int, Int), (EXPR, Expressions.SCRIPT, Iterable[CompilationError])] = - new Parser(version)(offset) - .parseExpressionWithErrorRecovery(input) - .flatMap { case (parseResult, removedCharPosOpt) => - compileExprWithCtx(parseResult.expr, saveExprContext, allowIllFormedStrings = false) - .run(ctx) - .value - ._2 - .map { compRes => - val errorList = - compRes.errors ++ - (if (compRes.t equivalent BOOLEAN) Nil else List(Generic(0, 0, "Script should return boolean"))) ++ - (if (removedCharPosOpt.isEmpty) - Nil - else - List( - Generic( - removedCharPosOpt.get.start, - removedCharPosOpt.get.end, - "Parsing failed. Some chars was removed as result of recovery process." - ) - )) - (compRes.expr, parseResult.copy(expr = compRes.parseNodeExpr), errorList) - } - .leftMap(e => (s"Compilation failed: ${Show[CompilationError].show(e)}", e.start, e.end)) - } - - def compileDecls(input: String, offset: LibrariesOffset, ctx: CompilerContext, version: StdLibVersion): Either[String, EXPR] = { - val adjustedDecls = s"$input\n${GlobalValNames.Unit}" - compileUntyped(adjustedDecls, offset, ctx, version) - } - - private def compileExpr(expr: Expressions.EXPR): CompileM[(Terms.EXPR, FINAL, Expressions.EXPR, Iterable[CompilationError])] = - compileExprWithCtx(expr, allowIllFormedStrings = false).map(r => (r.expr, r.t, r.parseNodeExpr, r.errors)) - +class ExpressionCompiler(val version: StdLibVersion) { private def compileExprWithCtx( expr: Expressions.EXPR, saveExprContext: Boolean = false, @@ -172,6 +78,10 @@ object ExpressionCompiler { } } + private def compileExpr(expr: Expressions.EXPR): CompileM[(Terms.EXPR, FINAL, Expressions.EXPR, Iterable[CompilationError])] = + compileExprWithCtx(expr, allowIllFormedStrings = false) + .map(r => (r.expr, r.t, r.parseNodeExpr, r.errors)) + private def compileIf( p: Pos, condExpr: Expressions.EXPR, @@ -353,63 +263,7 @@ object ExpressionCompiler { } } yield result - private def exprContainsRef(expr: Expressions.EXPR, ref: String): Boolean = - expr match { - case Expressions.GETTER(_, expr, _, _, _, _) => - exprContainsRef(expr, ref) - - case Expressions.BLOCK(_, decl, body, _, _) => - val refIsOverlappedByDecl = - decl.name match { - case PART.VALID(_, name) if name == ref => true - case _ => false - } - if (refIsOverlappedByDecl) false - else { - val declContainsRef = - decl match { - case Expressions.LET(_, _, value, _, _) => - exprContainsRef(value, ref) - case Expressions.FUNC(_, expr, _, args) => - val refIsOverlappedByArg = - args.exists { - case (PART.VALID(_, name), _) if name == ref => true - case _ => false - } - if (!refIsOverlappedByArg) exprContainsRef(expr, ref) - else false - } - declContainsRef || exprContainsRef(body, ref) - } - - case Expressions.IF(_, cond, ifTrue, ifFalse, _, _) => - exprContainsRef(cond, ref) || - exprContainsRef(ifTrue, ref) || - exprContainsRef(ifFalse, ref) - - case Expressions.FUNCTION_CALL(_, _, args, _, _) => - args.exists(exprContainsRef(_, ref)) - - case Expressions.REF(_, PART.VALID(_, name), _, _) if name == ref => - true - - case BINARY_OP(_, a, _, b, _, _) => - exprContainsRef(a, ref) || exprContainsRef(b, ref) - - case Expressions.MATCH(_, matchingExpr, cases, _, _) => - exprContainsRef(matchingExpr, ref) || - cases.exists { - case MATCH_CASE(_, TypedVar(Some(PART.VALID(_, varName)), _), caseExpr, _, _) if varName != ref => - exprContainsRef(caseExpr, ref) - case MATCH_CASE(_, TypedVar(None, _), caseExpr, _, _) => - exprContainsRef(caseExpr, ref) - case _ => false - } - - case _ => false - } - - def compileBlock( + private def compileBlock( pos: Expressions.Pos, declaration: Expressions.Declaration, expr: Expressions.EXPR, @@ -439,7 +293,12 @@ object ExpressionCompiler { _.getBytes(StandardCharsets.UTF_8).length <= ContractLimits.MaxDeclarationNameInBytes ) - def compileLet(p: Pos, let: Expressions.LET, saveExprContext: Boolean, allowIllFormedStrings: Boolean): CompileM[CompilationStepResultDec] = + protected def compileLet( + p: Pos, + let: Expressions.LET, + saveExprContext: Boolean, + allowIllFormedStrings: Boolean + ): CompileM[CompilationStepResultDec] = for { _ <- checkDeclarationNameSize(p, let) letNameWithErr <- validateShadowing(p, let).handleError() @@ -458,7 +317,7 @@ object ExpressionCompiler { } } yield result - def compileFunc( + protected def compileFunc( p: Pos, func: Expressions.FUNC, saveExprContext: Boolean, @@ -508,10 +367,10 @@ object ExpressionCompiler { } yield (result, argTypesWithErr._1.map(_.map(nameAnfInfo => (nameAnfInfo._1, nameAnfInfo._2.vType))).getOrElse(List.empty)) } - def updateCtx(letName: String, letType: Types.FINAL, p: Pos): CompileM[Unit] = + protected def updateCtx(letName: String, letType: Types.FINAL, p: Pos): CompileM[Unit] = modify[Id, CompilerContext, CompilationError](vars.modify(_)(_ + (letName -> VariableInfo(p, letType)))) - def updateCtx(funcName: String, typeSig: FunctionTypeSignature, p: Pos): CompileM[Unit] = + protected def updateCtx(funcName: String, typeSig: FunctionTypeSignature, p: Pos): CompileM[Unit] = modify[Id, CompilerContext, CompilationError](functions.modify(_)(_ + (funcName -> FunctionInfo(p, List(typeSig))))) private def compileLetBlock( @@ -753,12 +612,7 @@ object ExpressionCompiler { } } - def mkIf(p: Pos, cond: EXPR, ifTrue: (EXPR, FINAL), ifFalse: (EXPR, FINAL)): Either[CompilationError, (EXPR, FINAL)] = { - val t = TypeInferrer.findCommonType(ifTrue._2, ifFalse._2) - (IF(cond, ifTrue._1, ifFalse._1), t).asRight - } - - def mkIfCases( + private def mkIfCases( cases: List[MATCH_CASE], caseTypes: List[FINAL], refTmp: Expressions.REF, @@ -840,7 +694,8 @@ object ExpressionCompiler { val typeIf = tTypes.foldLeft(isInst(hType))((other, matchType) => BINARY_OP(mc.position, isInst(matchType), BinaryOperation.OR_OP, other)) Right(makeIfCase(typeIf, blockWithNewVar, further)) - case Nil => ??? + case Nil => + ??? } } yield cases case (_: TypedVar, t) => @@ -849,51 +704,49 @@ object ExpressionCompiler { case (ConstsPat(consts, _), _) => val cond = consts .map(c => BINARY_OP(mc.position, c, BinaryOperation.EQ_OP, refTmp)) - .reduceRight((c, r) => BINARY_OP(mc.position, c, BinaryOperation.OR_OP, r)) + .reduceRight(BINARY_OP(mc.position, _, BinaryOperation.OR_OP, _)) Right(makeIfCase(cond, blockWithNewVar, further)) case (p: CompositePattern, _) => - val pos = p.position - val newRef = p.caseType.fold(refTmp)(t => refTmp.copy(resultType = Some(caseType))) - val conditions = makeConditionsFromCompositePattern(p, newRef) - val cond = if (conditions.isEmpty) { - Expressions.TRUE(pos): Expressions.EXPR - } else { - conditions.reduceRight { (c, r) => - BINARY_OP(pos, c, BinaryOperation.AND_OP, r): Expressions.EXPR + val pos = p.position + val newRef = p.caseType.fold(refTmp)(_ => refTmp.copy(resultType = Some(caseType))) + makeConditionsFromCompositePattern(ctx, p, newRef) + .map { conditions => + val cond = + if (conditions.isEmpty) + Expressions.TRUE(pos) + else + conditions.reduceRight(BINARY_OP(pos, _, BinaryOperation.AND_OP, _)) + val checkingCond = + if (p.isInstanceOf[TuplePat]) { + val (resolvedTypes, size) = resolveTypesFromCompositePattern(p) + if (p.patternsWithFields.size == size) { + val typeChecks = + resolvedTypes + .map(t => Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(refTmp, Expressions.CONST_STRING(pos, t)))) + .reduceLeft[Expressions.EXPR] { case (c, r) => BINARY_OP(pos, c, BinaryOperation.OR_OP, r) } + BINARY_OP(pos, cond, BinaryOperation.AND_OP, typeChecks) + } else { + val size = Expressions.CONST_LONG(pos, p.patternsWithFields.size) + val getSize = Expressions.FUNCTION_CALL(pos, PART.VALID(pos, "size"), List(refTmp)) + val compareSize = BINARY_OP(pos, getSize, BinaryOperation.EQ_OP, size) + BINARY_OP(pos, cond, BinaryOperation.AND_OP, compareSize) + } + } else + cond + makeIfCase( + p.caseType.fold(checkingCond)(t => + BINARY_OP( + pos, + Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(refTmp, Expressions.CONST_STRING(pos, t.name))), + BinaryOperation.AND_OP, + Expressions.BLOCK(pos, Expressions.LET(pos, newRef.key, newRef, Some(caseType), true), checkingCond) + ) + ), + blockWithNewVar, + further + ) } - } - val checkingCond = - if (p.isInstanceOf[TuplePat]) { - val (resolvedTypes, size) = resolveTypesFromCompositePattern(p) - if (p.patternsWithFields.size == size) { - val typeChecks = - resolvedTypes - .map(t => Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(refTmp, Expressions.CONST_STRING(pos, t)))) - .reduceLeft[Expressions.EXPR] { case (c, r) => BINARY_OP(pos, c, BinaryOperation.OR_OP, r) } - BINARY_OP(pos, cond, BinaryOperation.AND_OP, typeChecks) - } else { - val size = Expressions.CONST_LONG(pos, p.patternsWithFields.size) - val getSize = Expressions.FUNCTION_CALL(pos, PART.VALID(pos, "size"), List(refTmp)) - val compareSize = BINARY_OP(pos, getSize, BinaryOperation.EQ_OP, size) - BINARY_OP(pos, cond, BinaryOperation.AND_OP, compareSize) - } - } else - cond - Right( - makeIfCase( - p.caseType.fold(checkingCond)(t => - BINARY_OP( - pos, - Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(refTmp, Expressions.CONST_STRING(pos, t.name))), - BinaryOperation.AND_OP, - Expressions.BLOCK(pos, Expressions.LET(pos, newRef.key, newRef, Some(caseType), true), checkingCond) - ) - ), - blockWithNewVar, - further - ) - ) } } } @@ -923,47 +776,56 @@ object ExpressionCompiler { Expressions.GETTER(pos, exp, field, checkObjectType = false) } - private def makeConditionsFromCompositePattern(p: CompositePattern, newRef: Expressions.REF): Seq[Expressions.EXPR] = - p.subpatterns collect { + private def makeConditionsFromCompositePattern( + ctx: CompilerContext, + p: CompositePattern, + newRef: Expressions.REF + ): Either[Generic, Seq[Expressions.EXPR]] = + p.subpatterns.traverse { case (pat @ TypedVar(_, Expressions.Union(types)), path) if types.nonEmpty => val pos = pat.position val v = mkGet(path, newRef, pos) - types - .map { + val r = types + .collect { case Expressions.Single(t, None) => - Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(v, Expressions.CONST_STRING(pos, t))): Expressions.EXPR + Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(v, Expressions.CONST_STRING(pos, t))) case Expressions.Single(PART.VALID(pos, Type.ListTypeName), Some(PART.VALID(_, Expressions.AnyType(_)))) => val t = PART.VALID(pos, "List[Any]") - Expressions.FUNCTION_CALL( - pos, - PART.VALID(pos, IsInstanceOf), - List(v, Expressions.CONST_STRING(pos, t)) - ): Expressions.EXPR - case _ => ??? - } - .reduceRight[Expressions.EXPR] { (c, r) => - BINARY_OP(pos, c, BinaryOperation.OR_OP, r) + Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(v, Expressions.CONST_STRING(pos, t))) } + .reduceRight[Expressions.EXPR](BINARY_OP(pos, _, BinaryOperation.OR_OP, _)) + Right(r) case (pat @ TypedVar(_, Expressions.Single(PART.VALID(_, Type.ListTypeName), Some(PART.VALID(_, Expressions.AnyType(_))))), path) => val pos = pat.position val v = mkGet(path, newRef, pos) val t = PART.VALID(pos, "List[Any]") - Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(v, Expressions.CONST_STRING(pos, t))): Expressions.EXPR + val r = Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(v, Expressions.CONST_STRING(pos, t))) + Right(r) case (pat @ TypedVar(_, Expressions.Single(t, None)), path) => val pos = pat.position val v = mkGet(path, newRef, pos) - Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(v, Expressions.CONST_STRING(pos, t))): Expressions.EXPR - case (TypedVar(_, Expressions.Single(_, _)), _) => ??? + val r = Expressions.FUNCTION_CALL(pos, PART.VALID(pos, IsInstanceOf), List(v, Expressions.CONST_STRING(pos, t))) + Right(r) case (pat @ ConstsPat(consts, _), path) => val pos = pat.position val v = mkGet(path, newRef, pos) consts - .map { c => - BINARY_OP(pos, c, BinaryOperation.EQ_OP, v) - } - .reduceRight[BINARY_OP] { (c, r) => - BINARY_OP(pos, c, BinaryOperation.OR_OP, r) + .traverse { + case const @ ( + _: Expressions.CONST_LONG | _: Expressions.CONST_STRING | _: Expressions.CONST_BYTESTR | _: Expressions.TRUE | + _: Expressions.FALSE | _: Expressions.REF + ) => + Right(BINARY_OP(pos, const, BinaryOperation.EQ_OP, v)) + case func @ Expressions.FUNCTION_CALL(pos, PART.VALID(_, name), _, _, _) if ctx.predefTypes.contains(name) => + Right(BINARY_OP(pos, func, BinaryOperation.EQ_OP, v)) + case expr if version < V8 => + Right(BINARY_OP(pos, expr, BinaryOperation.EQ_OP, v)) + case expr => + Left(Generic(expr.position.start, expr.position.end, "Only constant value could be matched with object field")) } + .map(_.reduceRight(BINARY_OP(pos, _, BinaryOperation.OR_OP, _))) + case _ => + Right(Expressions.TRUE(newRef.position)) } private def resolveTypesFromCompositePattern(p: CompositePattern): (Seq[PART[String]], Int) = { @@ -1070,27 +932,102 @@ object ExpressionCompiler { types.toList .traverse(handleCompositeType(pos, _, expectedType, varName)) .map(types => TUPLE(types)) - case Expressions.AnyType(pos) => (ANY: FINAL).pure[CompileM] + case _: Expressions.AnyType => + (ANY: FINAL).pure[CompileM] } - def handlePart[T](part: PART[T]): CompileM[T] = part match { + protected def handlePart[T](part: PART[T]): CompileM[T] = part match { case PART.VALID(_, x) => x.pure[CompileM] case PART.INVALID(p, message) => raiseError(Generic(p.start, p.end, message)) } +} +object ExpressionCompiler { implicit class RichBoolean(val b: Boolean) extends AnyVal { final def toOption[A](a: => A): Option[A] = if (b) Some(a) else None } - def apply(c: CompilerContext, expr: Expressions.EXPR, allowIllFormedStrings: Boolean = false): Either[String, (EXPR, FINAL)] = - applyWithCtx(c, expr, allowIllFormedStrings).map(r => (r._2, r._3)) + def compileWithParseResult( + input: String, + offset: LibrariesOffset, + ctx: CompilerContext, + version: StdLibVersion, + saveExprContext: Boolean = true + ): Either[(String, Int, Int), (EXPR, Expressions.SCRIPT, Iterable[CompilationError])] = + new Parser(version)(offset) + .parseExpressionWithErrorRecovery(input) + .flatMap { case (parseResult, removedCharPosOpt) => + new ExpressionCompiler(version) + .compileExprWithCtx(parseResult.expr, saveExprContext, allowIllFormedStrings = false) + .run(ctx) + .value + ._2 + .map { compRes => + val errorList = + compRes.errors ++ + (if (compRes.t equivalent BOOLEAN) Nil else List(Generic(0, 0, "Script should return boolean"))) ++ + (if (removedCharPosOpt.isEmpty) + Nil + else + List( + Generic( + removedCharPosOpt.get.start, + removedCharPosOpt.get.end, + "Parsing failed. Some chars was removed as result of recovery process." + ) + )) + (compRes.expr, parseResult.copy(expr = compRes.parseNodeExpr), errorList) + } + .leftMap(e => (s"Compilation failed: ${Show[CompilationError].show(e)}", e.start, e.end)) + } + + def compile( + input: String, + offset: LibrariesOffset, + ctx: CompilerContext, + version: StdLibVersion, + allowIllFormedStrings: Boolean = false + ): Either[String, (EXPR, FINAL)] = { + val parser = new Parser(version)(offset) + parser.parseExpr(input) match { + case fastparse.Parsed.Success(expr, _) => apply(ctx, version, expr, allowIllFormedStrings) + case f: fastparse.Parsed.Failure => Left(parser.toString(input, f)) + } + } + + def compileBoolean(input: String, offset: LibrariesOffset, ctx: CompilerContext, version: StdLibVersion): Either[String, EXPR] = { + compile(input, offset, ctx, version).flatMap { + case (expr, BOOLEAN) => Right(expr) + case _ => Left("Script should return boolean") + } + } + + def compileUntyped(input: String, offset: LibrariesOffset, ctx: CompilerContext, version: StdLibVersion): Either[String, EXPR] = { + compile(input, offset, ctx, version) + .map { case (expr, _) => expr } + } + + def compileDecls(input: String, offset: LibrariesOffset, ctx: CompilerContext, version: StdLibVersion): Either[String, EXPR] = { + val adjustedDecls = s"$input\n${GlobalValNames.Unit}" + compileUntyped(adjustedDecls, offset, ctx, version) + } + + def apply( + c: CompilerContext, + version: StdLibVersion, + expr: Expressions.EXPR, + allowIllFormedStrings: Boolean = false + ): Either[String, (EXPR, FINAL)] = + applyWithCtx(c, version, expr, allowIllFormedStrings).map(r => (r._2, r._3)) def applyWithCtx( c: CompilerContext, + version: StdLibVersion, expr: Expressions.EXPR, allowIllFormedStrings: Boolean = false ): Either[String, (CompilerContext, EXPR, FINAL)] = - compileExprWithCtx(expr, allowIllFormedStrings = allowIllFormedStrings) + new ExpressionCompiler(version) + .compileExprWithCtx(expr, allowIllFormedStrings = allowIllFormedStrings) .run(c) .value ._2 diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/TypeCast.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/TypeCast.scala index 38b6207c034..eea379da395 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/TypeCast.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/compiler/TypeCast.scala @@ -1,7 +1,6 @@ package com.wavesplatform.lang.v1.compiler import com.wavesplatform.common.utils.* import com.wavesplatform.lang.v1.compiler.CompilationError.{GenericFunctionNotFound, TypeCastAllowedOnlyForGenericList} -import com.wavesplatform.lang.v1.compiler.ExpressionCompiler.CompilationStepResultExpr import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.Types.* import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames diff --git a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/parser/Parser.scala b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/parser/Parser.scala index 2322e5808ae..45667b8781e 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/lang/v1/parser/Parser.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/lang/v1/parser/Parser.scala @@ -308,10 +308,11 @@ class Parser(stdLibVersion: StdLibVersion)(implicit offset: LibrariesOffset) { }) ).?.map(_.getOrElse(Union(Seq()))) + val objPatMin = if (stdLibVersion >= V8) 1 else 0 def pattern(implicit c: fastparse.P[Any]): P[Pattern] = (varDefP ~ comment ~ typesDefP).map { case (v, t) => TypedVar(v, t) } | (Index ~ "(" ~ pattern.rep(min = 2, sep = ",") ~/ ")" ~ Index).map(p => TuplePat(p._2, Pos(p._1, p._3))) | - (Index ~ anyVarName() ~ "(" ~ (anyVarName() ~ "=" ~ pattern).rep(sep = ",") ~ ")" ~ Index) + (Index ~ anyVarName() ~ "(" ~ (anyVarName() ~ "=" ~ pattern).rep(min = objPatMin, sep = ",") ~ ")" ~ Index) .map(p => ObjPat(p._3.map(kp => (PART.toOption(kp._1).get, kp._2)).toMap, Single(p._2, None), Pos(p._1, p._4))) | (Index ~ baseExpr.rep(min = 1, sep = "|") ~ Index).map(p => ConstsPat(p._2, Pos(p._1, p._3))) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala index 5874a51e302..d78e73802aa 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/DecompilerTest.scala @@ -561,7 +561,7 @@ class DecompilerTest extends PropSpec { def compileExpr(code: String, v: StdLibVersion = V3): Either[String, (EXPR, TYPE)] = { val untyped = Parser.parseExpr(code).get.value - val typed = ExpressionCompiler(getTestContext(v).compilerContext, untyped) + val typed = ExpressionCompiler(getTestContext(v).compilerContext, v, untyped) typed } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ErrorTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ErrorTest.scala index 34180e1189f..e3c17978cc7 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ErrorTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ErrorTest.scala @@ -3,7 +3,7 @@ package com.wavesplatform.lang.compiler import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.Common.multiplierFunction import com.wavesplatform.lang.contract.DApp -import com.wavesplatform.lang.directives.values.V5 +import com.wavesplatform.lang.directives.values.{V3, V5} import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, TestCompiler} import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames import com.wavesplatform.lang.v1.parser.BinaryOperation.SUM_OP @@ -158,7 +158,7 @@ class ErrorTest extends PropSpec { private def errorTests(exprs: ((String, String), Expressions.EXPR)*): Unit = exprs.foreach { case ((label, error), input) => property(s"Error: $label") { - ExpressionCompiler(compilerContext, input) should produce(error) + ExpressionCompiler(compilerContext, V3, input) should produce(error) } } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala index 871e4ba353f..db285b5f704 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ExpressionCompilerV1Test.scala @@ -35,7 +35,7 @@ class ExpressionCompilerV1Test extends PropSpec { property("should infer generic function return type") { import com.wavesplatform.lang.v1.parser.Expressions.* - val v = ExpressionCompiler(compilerContext, FUNCTION_CALL(AnyPos, PART.VALID(AnyPos, idT.name), List(CONST_LONG(AnyPos, 1)))).explicitGet() + val v = ExpressionCompiler(compilerContext, V3, FUNCTION_CALL(AnyPos, PART.VALID(AnyPos, idT.name), List(CONST_LONG(AnyPos, 1)))).explicitGet() v._2 shouldBe LONG } @@ -44,6 +44,7 @@ class ExpressionCompilerV1Test extends PropSpec { val v = ExpressionCompiler( compilerContext, + V3, FUNCTION_CALL( AnyPos, PART.VALID(AnyPos, "getElement"), @@ -59,7 +60,7 @@ class ExpressionCompilerV1Test extends PropSpec { } val expectedResult = Right(LONG) - ExpressionCompiler(compilerContext, expr).map(_._2) match { + ExpressionCompiler(compilerContext, V3, expr).map(_._2) match { case Right(x) => Right(x) shouldBe expectedResult case e @ Left(_) => e shouldBe expectedResult } @@ -68,11 +69,11 @@ class ExpressionCompilerV1Test extends PropSpec { property("string limit") { val maxString = "a" * Terms.DataEntryValueMax val expr = Parser.parseExpr(s""" "$maxString" """).get.value - ExpressionCompiler(compilerContext, expr).map(_._1) shouldBe CONST_STRING(maxString) + ExpressionCompiler(compilerContext, V3, expr).map(_._1) shouldBe CONST_STRING(maxString) val tooBigString = maxString + "a" val expr2 = Parser.parseExpr(s""" "$tooBigString" """).get.value - ExpressionCompiler(compilerContext, expr2) should produce("String size=32768 exceeds 32767 bytes") + ExpressionCompiler(compilerContext, V3, expr2) should produce("String size=32768 exceeds 32767 bytes") } @@ -94,8 +95,8 @@ class ExpressionCompilerV1Test extends PropSpec { """.stripMargin Parser.parseExpr(script).get.value } - ExpressionCompiler(compilerContext, funcExpr) should produce(s"Function '$tooLongName' size = 256 bytes exceeds 255") - ExpressionCompiler(compilerContext, letExpr) should produce(s"Let '$tooLongName' size = 256 bytes exceeds 255") + ExpressionCompiler(compilerContext, V3, funcExpr) should produce(s"Function '$tooLongName' size = 256 bytes exceeds 255") + ExpressionCompiler(compilerContext, V3, letExpr) should produce(s"Let '$tooLongName' size = 256 bytes exceeds 255") } @@ -117,18 +118,18 @@ class ExpressionCompilerV1Test extends PropSpec { """.stripMargin Parser.parseExpr(script).get.value } - ExpressionCompiler(compilerContext, funcExpr) shouldBe Symbol("right") - ExpressionCompiler(compilerContext, letExpr) shouldBe Symbol("right") + ExpressionCompiler(compilerContext, V3, funcExpr) shouldBe Symbol("right") + ExpressionCompiler(compilerContext, V3, letExpr) shouldBe Symbol("right") } property("tuple type checks") { val script = """ ("a", true, 123, base58'aaaa')._3 == true """ val expr = Parser.parseExpr(script).get.value - ExpressionCompiler(compilerContextV4, expr) should produce("Can't match inferred types of T over Int, Boolean") + ExpressionCompiler(compilerContextV4, V4, expr) should produce("Can't match inferred types of T over Int, Boolean") val script2 = """ ("a", true, 123, base58'aaaa') == ("a", true, "b", base58'aaaa') """ val expr2 = Parser.parseExpr(script2).get.value - ExpressionCompiler(compilerContextV4, expr2) should produce( + ExpressionCompiler(compilerContextV4, V4, expr2) should produce( "Can't match inferred types of T over (String, Boolean, Int, ByteVector), (String, Boolean, String, ByteVector)" ) @@ -146,7 +147,7 @@ class ExpressionCompilerV1Test extends PropSpec { | (((v, q), (true, v)), q) == (((1, true), (q, q)), v) """.stripMargin val expr3 = Parser.parseExpr(script3).get.value - ExpressionCompiler(compilerContextV4, expr3) shouldBe Symbol("right") + ExpressionCompiler(compilerContextV4, V4, expr3) shouldBe Symbol("right") val script4 = """ @@ -156,7 +157,7 @@ class ExpressionCompilerV1Test extends PropSpec { | (((v, q), (true, v)), q) == (((1, true), (v, q)), v) """.stripMargin val expr4 = Parser.parseExpr(script4).get.value - ExpressionCompiler(compilerContextV4, expr4) should produce( + ExpressionCompiler(compilerContextV4, V4, expr4) should produce( "Can't match inferred types of T over " + "(((Int|String, Boolean|Int|String), (Boolean, Int|String)), Boolean|Int|String), " + "(((Int, Boolean), (Int|String, Boolean|Int|String)), Int|String) in 102-154" @@ -171,7 +172,7 @@ class ExpressionCompilerV1Test extends PropSpec { | a1 == b1 """.stripMargin val expr = Parser.parseExpr(script).get.value - ExpressionCompiler(compilerContextV4, expr) shouldBe Symbol("right") + ExpressionCompiler(compilerContextV4, V4, expr) shouldBe Symbol("right") } property("function with tuple args") { @@ -189,7 +190,7 @@ class ExpressionCompilerV1Test extends PropSpec { | """.stripMargin val expr = Parser.parseExpr(script).get.value - ExpressionCompiler(compilerContextV4, expr) shouldBe Symbol("right") + ExpressionCompiler(compilerContextV4, V4, expr) shouldBe Symbol("right") val script2 = """ @@ -200,7 +201,7 @@ class ExpressionCompilerV1Test extends PropSpec { | """.stripMargin val expr2 = Parser.parseExpr(script2).get.value - ExpressionCompiler(compilerContextV4, expr2) should produce( + ExpressionCompiler(compilerContextV4, V4, expr2) should produce( "Non-matching types: expected: (String, Boolean), actual: Boolean in 69-86" ) @@ -213,7 +214,7 @@ class ExpressionCompilerV1Test extends PropSpec { | """.stripMargin val expr3 = Parser.parseExpr(script3).get.value - ExpressionCompiler(compilerContextV4, expr3) should produce( + ExpressionCompiler(compilerContextV4, V4, expr3) should produce( "Non-matching types: expected: ((Int, String), Boolean)|(Int, String, Boolean), actual: (Int, String) in 73-84" ) } @@ -237,7 +238,7 @@ class ExpressionCompilerV1Test extends PropSpec { | """.stripMargin val expr = Parser.parseExpr(script).get.value - ExpressionCompiler(compilerContextV4, expr) shouldBe Symbol("right") + ExpressionCompiler(compilerContextV4, V4, expr) shouldBe Symbol("right") val script2 = """ @@ -249,7 +250,7 @@ class ExpressionCompilerV1Test extends PropSpec { | true """.stripMargin val expr2 = Parser.parseExpr(script2).get.value - ExpressionCompiler(compilerContextV4, expr2) should produce( + ExpressionCompiler(compilerContextV4, V4, expr2) should produce( "Matching not exhaustive: " + "possibleTypes are (Int, String), ((Boolean, Int), ByteVector), " + "while matched are (Int, String), (Boolean, Int, ByteVector)" @@ -274,7 +275,7 @@ class ExpressionCompilerV1Test extends PropSpec { | """.stripMargin val expr3 = Parser.parseExpr(script3).get.value - ExpressionCompiler(compilerContextV4, expr3) shouldBe Symbol("right") + ExpressionCompiler(compilerContextV4, V4, expr3) shouldBe Symbol("right") val script4 = """ @@ -288,7 +289,7 @@ class ExpressionCompilerV1Test extends PropSpec { | """.stripMargin val expr4 = Parser.parseExpr(script4).get.value - ExpressionCompiler(compilerContextV4, expr4) should produce( + ExpressionCompiler(compilerContextV4, V4, expr4) should produce( "Matching not exhaustive: " + "possibleTypes are (Int, String), (Boolean, String), (ByteVector, Boolean, (String, (Int, Boolean))|Int), " + "while matched are (Boolean|Int, String), (ByteVector, Boolean, Int), (ByteVector, Boolean, (String, Int, Boolean)) " + @@ -303,7 +304,7 @@ class ExpressionCompilerV1Test extends PropSpec { | } """.stripMargin val expr5 = Parser.parseExpr(script5).get.value - ExpressionCompiler(compilerContextV4, expr5) shouldBe Symbol("right") + ExpressionCompiler(compilerContextV4, V4, expr5) shouldBe Symbol("right") } property("JS API compile limit exceeding error") { @@ -330,6 +331,7 @@ class ExpressionCompilerV1Test extends PropSpec { def checkExtract(version: StdLibVersion) = ExpressionCompiler( getTestContext(version).compilerContext, + version, Parser.parseExpr(" extract(1) ").get.value ) @@ -358,7 +360,7 @@ class ExpressionCompilerV1Test extends PropSpec { DirectiveDictionary[StdLibVersion].all .foreach { version => - val result = ExpressionCompiler(getTestContext(version).compilerContext, Parser.parseExpr(expr).get.value) + val result = ExpressionCompiler(getTestContext(version).compilerContext, version, Parser.parseExpr(expr).get.value) if (version >= V4) result shouldBe Symbol("right") else @@ -390,7 +392,7 @@ class ExpressionCompilerV1Test extends PropSpec { version <- DirectiveDictionary[StdLibVersion].all scriptType <- DirectiveDictionary[ScriptType].all } { - val result = ExpressionCompiler(getTestContext(version, scriptType).compilerContext, expr(version, scriptType)) + val result = ExpressionCompiler(getTestContext(version, scriptType).compilerContext, version, expr(version, scriptType)) if (version < V5 || scriptType != Account) result.swap.getOrElse(???).split("Can't find a function").length shouldBe 9 else @@ -415,7 +417,7 @@ class ExpressionCompilerV1Test extends PropSpec { DirectiveDictionary[StdLibVersion].all .foreach { version => - val result = ExpressionCompiler(getTestContext(version).compilerContext, expr(version)) + val result = ExpressionCompiler(getTestContext(version).compilerContext, version, expr(version)) if (version < V5) result should produce( "Compilation failed: [" + @@ -446,7 +448,7 @@ class ExpressionCompilerV1Test extends PropSpec { DirectiveDictionary[StdLibVersion].all .foreach { version => - val result = ExpressionCompiler(getTestContext(version).compilerContext, expr(version)) + val result = ExpressionCompiler(getTestContext(version).compilerContext, version, expr(version)) if (version < V5) result shouldBe Symbol("right") else @@ -484,7 +486,7 @@ class ExpressionCompilerV1Test extends PropSpec { DirectiveDictionary[StdLibVersion].all .foreach { version => - ExpressionCompiler(getTestContext(version).compilerContext, expr(version)) shouldBe Symbol("right") + ExpressionCompiler(getTestContext(version).compilerContext, version, expr(version)) shouldBe Symbol("right") } } @@ -512,7 +514,7 @@ class ExpressionCompilerV1Test extends PropSpec { DirectiveDictionary[StdLibVersion].all .foreach { version => - val result = ExpressionCompiler(getTestContext(version).compilerContext, expr(version)) + val result = ExpressionCompiler(getTestContext(version).compilerContext, version, expr(version)) if (version < V5) result shouldBe Symbol("right") else { @@ -542,7 +544,7 @@ class ExpressionCompilerV1Test extends PropSpec { DirectiveDictionary[StdLibVersion].all .filter(_ >= V3) .foreach { version => - val result = ExpressionCompiler(getTestContext(version).compilerContext, expr(version)) + val result = ExpressionCompiler(getTestContext(version).compilerContext, version, expr(version)) val error = result.swap.getOrElse(???) error should include("Can't find a function 'invoke'") error should include("Can't find a function 'reentrantInvoke'") @@ -1149,7 +1151,7 @@ class ExpressionCompilerV1Test extends PropSpec { propertyName: String )(expr: Expressions.EXPR, expectedResult: Either[String, (EXPR, TYPE)] => org.scalatest.compatible.Assertion, ctx: CompilerContext): Unit = property(propertyName) { - val res = compiler.ExpressionCompiler(ctx, expr) + val res = compiler.ExpressionCompiler(ctx, V3, expr) expectedResult(res) } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala index 8ce9d375f90..b953caabf5f 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/compiler/ScriptPreprocessorTest.scala @@ -6,7 +6,6 @@ import cats.kernel.Monoid import com.wavesplatform.lang.directives.values.V3 import com.wavesplatform.lang.directives.{Directive, DirectiveParser} import com.wavesplatform.lang.script.ScriptPreprocessor -import com.wavesplatform.lang.v1.CTX import com.wavesplatform.lang.v1.compiler.ExpressionCompiler import com.wavesplatform.lang.v1.compiler.Terms.{CONST_BOOLEAN, EVALUATED} import com.wavesplatform.lang.v1.evaluator.Contextful.NoContext @@ -29,9 +28,9 @@ class ScriptPreprocessorTest extends PropSpec with ScriptGenParser { } yield r private def eval(code: String): Either[String, EVALUATED] = { - val untyped = Parser.parseExpr(code).get.value - val ctx: CTX[NoContext] = Monoid.combineAll(Seq(PureContext.build(V3, useNewPowPrecision = true))) - val typed = ExpressionCompiler(ctx.compilerContext, untyped) + val untyped = Parser.parseExpr(code).get.value + val ctx = Monoid.combineAll(Seq(PureContext.build(V3, useNewPowPrecision = true))) + val typed = ExpressionCompiler(ctx.compilerContext, V3, untyped) typed.flatMap(v => evaluator[EVALUATED](ctx.evaluationContext, v._1).leftMap(_.toString)) } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala index 5eae03001d6..ed72f994c1f 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorTestBase.scala @@ -57,7 +57,7 @@ class ScriptEstimatorTestBase(estimators: ScriptEstimator*) extends PropSpec { protected def compile(code: String)(implicit version: StdLibVersion): EXPR = { val untyped = Parser.parseExpr(code).get.value - ExpressionCompiler(ctx.compilerContext, untyped).map(_._1).explicitGet() + ExpressionCompiler(ctx.compilerContext, version, untyped).map(_._1).explicitGet() } protected def estimate( diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala index 8a56d49c8e4..059eb1f2b52 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorSpec.scala @@ -1,18 +1,18 @@ package com.wavesplatform.lang.evaluator -import cats.implicits.* import cats.Id +import cats.implicits.* import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.lang.{Common, ExecutionError} -import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} import com.wavesplatform.lang.directives.values.* +import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} import com.wavesplatform.lang.utils.lazyContexts import com.wavesplatform.lang.v1.compiler.ExpressionCompiler import com.wavesplatform.lang.v1.compiler.Terms.{EVALUATED, EXPR} import com.wavesplatform.lang.v1.evaluator.ContractEvaluator.LogExtraInfo import com.wavesplatform.lang.v1.evaluator.{EvaluatorV2, Log} -import com.wavesplatform.lang.v1.parser.Parser +import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset.NoLibraries import com.wavesplatform.lang.v1.testing.ScriptGen +import com.wavesplatform.lang.{Common, ExecutionError} import com.wavesplatform.test.PropSpec import org.scalatest.Inside import org.scalatest.exceptions.TestFailedException @@ -78,9 +78,8 @@ abstract class EvaluatorSpec extends PropSpec with ScriptGen with Inside { EvaluatorV2.applyCompleted(evalCtx, expr, LogExtraInfo(), version, correctFunctionCallScope = true, newMode = true, enableExecutionLog = false) } - def compile(code: String, version: StdLibVersion): Either[String, EXPR] = { - val ctx = lazyContexts((DirectiveSet(version, Account, Expression).explicitGet(), true, true)).value() - val parsed = Parser.parseExpr(code).get.value - ExpressionCompiler(ctx.compilerContext, parsed, allowIllFormedStrings = true).map(_._1) + private def compile(code: String, version: StdLibVersion): Either[String, EXPR] = { + val ctx = lazyContexts((DirectiveSet(version, Account, Expression).explicitGet(), true, true)).value() + ExpressionCompiler.compile(code, NoLibraries, ctx.compilerContext, version, allowIllFormedStrings = true).map(_._1) } } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala index 3c0dbc8745d..f040ca89b32 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV1V2Test.scala @@ -880,7 +880,7 @@ class EvaluatorV1V2Test extends PropSpec with EitherValues { evalPure[EVALUATED]( context.evaluationContext[Id], ExpressionCompiler - .apply(context.compilerContext, xs) + .apply(context.compilerContext, version, xs) .explicitGet() ._1 ) diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala index cd7b4ff3240..bc20e70fafc 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/EvaluatorV2Test.scala @@ -67,7 +67,7 @@ class EvaluatorV2Test extends PropSpec with Inside { private def compile(script: String): EXPR = { val parsed = Parser.parseExpr(script).get.value - ExpressionCompiler(ctx.compilerContext, parsed).explicitGet()._1 + ExpressionCompiler(ctx.compilerContext, version, parsed).explicitGet()._1 } property("multiple lets by step") { diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/NestedPatternsTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/NestedPatternsTest.scala index 080aded4dfe..b33b7575958 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/NestedPatternsTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/evaluator/NestedPatternsTest.scala @@ -1,6 +1,7 @@ package com.wavesplatform.lang.evaluator -import com.wavesplatform.lang.directives.values.{StdLibVersion, V6} +import com.wavesplatform.lang.directives.values.{StdLibVersion, V6, V7, V8} import com.wavesplatform.lang.v1.compiler.Terms.CONST_BOOLEAN +import com.wavesplatform.test.produce class NestedPatternsTest extends EvaluatorSpec { implicit val v: StdLibVersion = V6 @@ -126,4 +127,58 @@ class NestedPatternsTest extends EvaluatorSpec { """.stripMargin ) shouldBe Right(CONST_BOOLEAN(true)) } + + property("restrictions from V8") { + val script1 = + s""" + | match Lease(Address(base58''), 1, 1) { + | case Lease(nonce = f()) => true + | case _ => false + | } + """.stripMargin + eval(script1)(V7, checkNext = false) shouldBe Right(CONST_BOOLEAN(true)) + eval(script1)(V8) should produce("Only constant value could be matched with object field in 63-66") + + val script2 = + s""" + | func f() = 777 + | match Lease(Address(base58''), 1, 1) { + | case Lease(nonce = f()) => true + | case _ => false + | } + """.stripMargin + eval(script2)(V7, checkNext = false) shouldBe Right(CONST_BOOLEAN(true)) + eval(script2)(V8) should produce("Only constant value could be matched with object field in 79-82") + + val script3 = + s""" + | func f() = 777 + | match Lease(Address(base58''), 1, 1) { + | case Lease(nonce = {f()}) => true + | case _ => false + | } + """.stripMargin + eval(script3)(V7, checkNext = false) shouldBe Right(CONST_BOOLEAN(false)) + eval(script3)(V8) should produce("Only constant value could be matched with object field in 80-83") + + val script4 = + s""" + | match Lease(Address(base58''), 1, 1) { + | case Lease(nonce = {1 + 2}) => true + | case _ => false + | } + """.stripMargin + eval(script4)(V7, checkNext = false) shouldBe Right(CONST_BOOLEAN(false)) + eval(script4)(V8) should produce("Only constant value could be matched with object field in 64-69") + + val script5 = + s""" + | match Lease(Address(base58''), 1, 1) { + | case Lease(nonce = {if (true) then 2 else 1}) => true + | case _ => false + | } + """.stripMargin + eval(script5)(V7, checkNext = false) shouldBe Right(CONST_BOOLEAN(false)) + eval(script5)(V8) should produce("Only constant value could be matched with object field in 64-87") + } } diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/serde/SerdeTest.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/serde/SerdeTest.scala index 0d9c56bd73e..8de49007e18 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/serde/SerdeTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/serde/SerdeTest.scala @@ -177,7 +177,7 @@ class SerdeTest extends FreeSpec { } private def roundTripTest(untypedExpr: Expressions.EXPR): Unit = { - val typedExpr = ExpressionCompiler(PureContext.build(V1, useNewPowPrecision = true).compilerContext, untypedExpr).map(_._1).explicitGet() + val typedExpr = ExpressionCompiler(PureContext.build(V1, useNewPowPrecision = true).compilerContext, V1, untypedExpr).map(_._1).explicitGet() roundTripTest(typedExpr) } diff --git a/lang/tests/src/test/scala/com/wavesplatform/utils/MerkleTest.scala b/lang/tests/src/test/scala/com/wavesplatform/utils/MerkleTest.scala index 23c3ae99426..95039717194 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/utils/MerkleTest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/utils/MerkleTest.scala @@ -109,7 +109,7 @@ class MerkleTest extends PropSpec { val untyped = Parser.parseExpr(code).get.value val ctx = lazyContexts((DirectiveSet(version, Account, Expression).explicitGet(), true, true))() val evalCtx = ctx.evaluationContext[Id](Common.emptyBlockchainEnvironment()) - val typed = ExpressionCompiler(ctx.compilerContext, untyped) + val typed = ExpressionCompiler(ctx.compilerContext, V3, untyped) typed.flatMap(v => EvaluatorV2.applyCompleted(evalCtx, v._1, LogExtraInfo(), version, true, true, false)._3.leftMap(_.toString)) } diff --git a/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala b/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala index 0f15a68e63e..570ff7ad77b 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/utils/RSATest.scala @@ -337,7 +337,7 @@ class RSATest extends PropSpec with BeforeAndAfterAll { private def eval[T <: EVALUATED](code: String, ctx: CTX[NoContext] = PureContext.build(V4, useNewPowPrecision = true) |+| CryptoContext.build(Global, V4)): Either[String, T] = { val untyped = Parser.parseExpr(code).get.value - val typed = ExpressionCompiler(ctx.compilerContext, untyped) + val typed = ExpressionCompiler(ctx.compilerContext, V4, untyped) typed.flatMap(v => evaluator[T](ctx.evaluationContext, v._1).leftMap(_.message)) } diff --git a/node-it/src/test/scala/com/wavesplatform/it/async/MicroblocksGenerationSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/async/MicroblocksGenerationSuite.scala index f14a72b6c59..c84c1be859b 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/async/MicroblocksGenerationSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/async/MicroblocksGenerationSuite.scala @@ -22,10 +22,7 @@ class MicroblocksGenerationSuite extends BaseFreeSpec with TransferSending { block <- miner.blockAt(2) } yield { block.transactions.size shouldBe maxTxs - - val blockTxs = block.transactions.map(_.id) - val diff = uploadedTxs.map(_.id).toSet -- blockTxs - diff shouldBe empty + block.transactions.map(_.id) should contain theSameElementsAs uploadedTxs.map(_.id).toSet }, 3.minutes ) diff --git a/node-it/src/test/scala/com/wavesplatform/it/async/WideStateGenerationSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/async/WideStateGenerationSuite.scala index 2ef4a1f2e21..a9688616178 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/async/WideStateGenerationSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/async/WideStateGenerationSuite.scala @@ -1,14 +1,13 @@ package com.wavesplatform.it.async -import java.util.concurrent.TimeoutException - import com.typesafe.config.{Config, ConfigFactory} -import com.wavesplatform.it._ -import com.wavesplatform.it.api.AsyncHttpApi._ -import com.wavesplatform.it.util._ +import com.wavesplatform.it.* +import com.wavesplatform.it.api.AsyncHttpApi.* +import com.wavesplatform.it.util.* +import java.util.concurrent.TimeoutException import scala.concurrent.Future.traverse -import scala.concurrent.duration._ +import scala.concurrent.duration.* import scala.concurrent.{Await, Future} @LoadTest @@ -66,15 +65,14 @@ class WideStateGenerationSuite extends BaseFreeSpec with WaitForHeight2 with Tra } yield () val limit = GlobalTimer.instance.schedule(Future.failed(new TimeoutException("Time is out for test")), 18.minutes) - val testWithDumps = Future.firstCompletedOf(Seq(test, limit)).recoverWith { - case e => - for { - _ <- dumpBalances() - dumps <- traverse(nodes)(dumpBlockChain) - } yield { - log.debug(dumps.mkString("Dumps:\n", "\n\n", "\n")) - throw e - } + val testWithDumps = Future.firstCompletedOf(Seq(test, limit)).recoverWith { case e => + for { + _ <- dumpBalances() + dumps <- traverse(nodes)(dumpBlockChain) + } yield { + log.debug(dumps.mkString("Dumps:\n", "\n\n", "\n")) + throw e + } } Await.result(testWithDumps, 18.minutes) @@ -84,12 +82,9 @@ class WideStateGenerationSuite extends BaseFreeSpec with WaitForHeight2 with Tra for { height <- node.height blocks <- node.blockSeq(1, height) - } yield { + } yield withClue(s"all transactions in node") { val txsInBlockchain = blocks.flatMap(_.transactions.map(_.id)) - val diff = txIds -- txsInBlockchain - withClue(s"all transactions in node") { - diff shouldBe empty - } + txIds -- txsInBlockchain shouldBe empty } } @@ -99,7 +94,7 @@ class WideStateGenerationSuite extends BaseFreeSpec with WaitForHeight2 with Tra }.map(_.toMap) .map { r => log.debug(s"""Balances: - |${r.map { case (config, balance) => s"${config.getString("address")} -> $balance" }.mkString("\n")}""".stripMargin) + |${r.map { case (config, balance) => s"${config.getString("address")} -> $balance" }.mkString("\n")}""".stripMargin) r } } diff --git a/node/src/main/scala/com/wavesplatform/api/common/AddressPortfolio.scala b/node/src/main/scala/com/wavesplatform/api/common/AddressPortfolio.scala index 5865baaf2fd..393936b02ac 100644 --- a/node/src/main/scala/com/wavesplatform/api/common/AddressPortfolio.scala +++ b/node/src/main/scala/com/wavesplatform/api/common/AddressPortfolio.scala @@ -13,6 +13,7 @@ import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.utils.ScorexLogging import java.nio.ByteBuffer +import scala.collection.immutable.VectorMap import scala.collection.mutable.ArrayBuffer import scala.jdk.CollectionConverters.* @@ -29,9 +30,8 @@ class NFTIterator(addressId: AddressId, maybeAfter: Option[IssuedAsset], resourc def skipEntry(key: Array[Byte]): Boolean = !key.endsWith(after.id.arr) - while (dbIterator.isValid && skipEntry(dbIterator.key())) { + while (dbIterator.isValid && skipEntry(dbIterator.key())) dbIterator.next() - } if (dbIterator.isValid && !skipEntry(dbIterator.key())) dbIterator.next() } @@ -99,19 +99,14 @@ class WavesBalanceIterator(addressId: AddressId, resource: DBResource) extends A class BalanceIterator( address: Address, underlying: Iterator[Seq[(IssuedAsset, Long)]], - includeAsset: IssuedAsset => Boolean, - private var pendingOverrides: Map[(Address, Asset), Long] + private var pendingOverrides: VectorMap[(Address, Asset), Long] ) extends AbstractIterator[Seq[(IssuedAsset, Long)]] { - private def nextOverride(): Seq[(IssuedAsset, Long)] = if (pendingOverrides.isEmpty) endOfData() else { - val balances = pendingOverrides.collect { - case ((`address`, asset: IssuedAsset), balance) if includeAsset(asset) => - asset -> balance - }.toSeq - pendingOverrides = Map.empty - balances + val assetsWithBalances = pendingOverrides.collect { case ((`address`, asset: IssuedAsset), balance) => asset -> balance }.toSeq + pendingOverrides = VectorMap.empty + assetsWithBalances } override def computeNext(): Seq[(IssuedAsset, Long)] = @@ -138,11 +133,15 @@ object AddressPortfolio { resource .get(Keys.addressId(address)) .fold(Iterator.empty[Seq[(IssuedAsset, Long)]])(addressId => new NFTIterator(addressId, maybeAfter, resource).asScala), - asset => loadAssetDescription(asset).exists(_.nft), snapshot.balances ).asScala - .map(_.collect { case (asset, balance) if balance > 0 => asset } - .flatMap(a => loadAssetDescription(a).map(a -> _))) + .map { assets => + maybeAfter + .filter(after => assets.exists(_._1 == after)) + .fold(assets)(after => assets.dropWhile(_._1 != after).drop(1)) + .collect { case (asset, balance) if balance > 0 => asset } + .flatMap(asset => loadAssetDescription(asset).collect { case description if description.nft => asset -> description }) + } def assetBalanceIterator( resource: DBResource, @@ -155,10 +154,9 @@ object AddressPortfolio { resource .get(Keys.addressId(address)) .fold(Iterator.empty[Seq[(IssuedAsset, Long)]])(addressId => new AssetBalanceIterator(addressId, resource).asScala), - includeAsset, snapshot.balances ).asScala - .map(_.filter { case (asset, balance) => - includeAsset(asset) && balance > 0 - }) + .map( + _.filter { case (asset, balance) => includeAsset(asset) && balance > 0 } + ) } diff --git a/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala b/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala index f3782f27070..22f2ee7d90f 100644 --- a/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala +++ b/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala @@ -329,7 +329,7 @@ object StateSnapshot { } .map(_.toMap) - private def assetStatics(issuedAssets: VectorMap[IssuedAsset, NewAssetInfo]): VectorMap[IssuedAsset, AssetStatic] = + def assetStatics(issuedAssets: VectorMap[IssuedAsset, NewAssetInfo]): VectorMap[IssuedAsset, AssetStatic] = issuedAssets.map { case (asset, info) => asset -> AssetStatic( diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala b/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala index d4d58d7adbb..66cc7a4043a 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala @@ -13,7 +13,7 @@ import com.wavesplatform.lang.script.v1.ExprScript import com.wavesplatform.lang.script.{ContractScript, Script} import com.wavesplatform.settings.FunctionalitySettings import com.wavesplatform.state.* -import com.wavesplatform.transaction.* +import com.wavesplatform.state.diffs.invoke.InvokeDiffsCommon import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.* import com.wavesplatform.transaction.assets.* @@ -22,6 +22,7 @@ import com.wavesplatform.transaction.lease.* import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.smart.{InvokeExpressionTransaction, InvokeScriptTransaction, SetScriptTransaction} import com.wavesplatform.transaction.transfer.* +import com.wavesplatform.transaction.{Asset, *} import scala.util.{Left, Right} @@ -36,21 +37,25 @@ object CommonValidation { feeAmount: Long, allowFeeOverdraft: Boolean = false ): Either[ValidationError, T] = { - val amountDiff = assetId match { + val amountPortfolio = assetId match { case aid @ IssuedAsset(_) => Portfolio.build(aid -> -amount) case Waves => Portfolio(-amount) } - val feeDiff = feeAssetId match { + val feePortfolio = feeAssetId match { case aid @ IssuedAsset(_) => Portfolio.build(aid -> -feeAmount) case Waves => Portfolio(-feeAmount) } val checkedTx = for { - spendings <- amountDiff.combine(feeDiff) + _ <- assetId match { + case IssuedAsset(id) => InvokeDiffsCommon.checkAsset(blockchain, id) + case Waves => Right(()) + } + spendings <- amountPortfolio.combine(feePortfolio) oldWavesBalance = blockchain.balance(sender, Waves) newWavesBalance <- safeSum(oldWavesBalance, spendings.balance, "Spendings") - feeUncheckedBalance <- safeSum(oldWavesBalance, amountDiff.balance, "Transfer amount") + feeUncheckedBalance <- safeSum(oldWavesBalance, amountPortfolio.balance, "Transfer amount") overdraftFilter = allowFeeOverdraft && feeUncheckedBalance >= 0 _ <- Either.cond( @@ -95,6 +100,7 @@ object CommonValidation { for { address <- blockchain.resolveAlias(citx.dApp) + _ <- InvokeDiffsCommon.checkPayments(blockchain, citx.payments) allowFeeOverdraft = blockchain.accountScript(address) match { case Some(AccountScriptInfo(_, ContractScriptImpl(version, _), _, _)) if version >= V4 && blockchain.useCorrectPaymentCheck => true case _ => false diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/EthereumTransactionDiff.scala b/node/src/main/scala/com/wavesplatform/state/diffs/EthereumTransactionDiff.scala index 382081b76d0..2d3f5b39cbc 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/EthereumTransactionDiff.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/EthereumTransactionDiff.scala @@ -8,7 +8,7 @@ import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.v1.serialization.SerdeV1 import com.wavesplatform.protobuf.transaction.{PBAmounts, PBRecipients} -import com.wavesplatform.state.diffs.invoke.InvokeScriptTransactionDiff +import com.wavesplatform.state.diffs.invoke.{InvokeDiffsCommon, InvokeScriptTransactionDiff} import com.wavesplatform.state.{Blockchain, StateSnapshot} import com.wavesplatform.transaction.EthereumTransaction import com.wavesplatform.transaction.TxValidationError.GenericError @@ -78,6 +78,7 @@ object EthereumTransactionDiff { for { _ <- checkLeadingZeros(tx, blockchain) invocation <- TracedResult(ei.toInvokeScriptLike(tx, blockchain)) + _ <- TracedResult(InvokeDiffsCommon.checkPayments(blockchain, invocation.payments)) snapshot <- InvokeScriptTransactionDiff(blockchain, currentBlockTs, limitedExecution, enableExecutionLog)(invocation) resultSnapshot <- TransactionDiffer.assetsVerifierDiff( blockchain, diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala index e154bb12c44..b00d78891e5 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala @@ -30,6 +30,7 @@ import com.wavesplatform.transaction.TxValidationError.* import com.wavesplatform.transaction.assets.IssueTransaction import com.wavesplatform.transaction.smart.* import com.wavesplatform.transaction.smart.DAppEnvironment.ActionLimits +import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.smart.script.ScriptRunner import com.wavesplatform.transaction.smart.script.ScriptRunner.TxOrd import com.wavesplatform.transaction.smart.script.trace.AssetVerifierTrace.AssetContext @@ -345,6 +346,15 @@ object InvokeDiffsCommon { ) } + def checkPayments(blockchain: Blockchain, payments: Seq[Payment]): Either[GenericError, Unit] = + payments + .collectFirstSome { + case Payment(_, IssuedAsset(id)) => InvokeDiffsCommon.checkAsset(blockchain, id).swap.toOption + case Payment(_, Waves) => None + } + .map(GenericError(_)) + .toLeft(()) + def checkAsset(blockchain: Blockchain, assetId: ByteStr): Either[String, Unit] = if (blockchain.isFeatureActivated(BlockchainFeatures.SynchronousCalls)) if (assetId.size != AssetIdLength) @@ -464,8 +474,8 @@ object InvokeDiffsCommon { if (remainingLimit < Int.MaxValue) remainingLimit - currentSnapshot.scriptsComplexity.toInt else remainingLimit - val blockchain = SnapshotBlockchain(sblockchain, currentSnapshot) - val actionSender = Recipient.Address(ByteStr(dAppAddress.bytes)) + val blockchain = SnapshotBlockchain(sblockchain, currentSnapshot) + val actionSender = Recipient.Address(ByteStr(dAppAddress.bytes)) def applyTransfer(transfer: AssetTransfer, pk: PublicKey): TracedResult[ValidationError, StateSnapshot] = { val AssetTransfer(addressRepr, recipient, amount, asset) = transfer diff --git a/node/src/test/scala/com/wavesplatform/BlockchainStubHelpers.scala b/node/src/test/scala/com/wavesplatform/BlockchainStubHelpers.scala index 55ff44fe6b6..bb990d1d03b 100644 --- a/node/src/test/scala/com/wavesplatform/BlockchainStubHelpers.scala +++ b/node/src/test/scala/com/wavesplatform/BlockchainStubHelpers.scala @@ -6,7 +6,6 @@ import com.wavesplatform.block.SignedBlockHeader import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.* import com.wavesplatform.features.{BlockchainFeature, BlockchainFeatures} -import com.wavesplatform.history.SnapshotOps.* import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.script.Script @@ -148,12 +147,9 @@ trait BlockchainStubHelpers { self: MockFactoryBase => ) } - def transactionDiffer(time: Time = SystemTime, withFailed: Boolean = false)(tx: Transaction): TracedResult[ValidationError, Diff] = { - val snapshot = - if (withFailed) TransactionDiffer(Some(time.correctedTime()), time.correctedTime())(blockchain, tx) - else TransactionDiffer.forceValidate(Some(time.correctedTime()), time.correctedTime())(blockchain, tx) - snapshot.map(_.toDiff(blockchain)) - } + def transactionDiffer(time: Time = SystemTime, withFailed: Boolean = false)(tx: Transaction): TracedResult[ValidationError, StateSnapshot] = + if (withFailed) TransactionDiffer(Some(time.correctedTime()), time.correctedTime())(blockchain, tx) + else TransactionDiffer.forceValidate(Some(time.correctedTime()), time.correctedTime())(blockchain, tx) def transactionPublisher(time: Time = SystemTime): TransactionPublisher = (tx: Transaction, _: Option[Channel]) => { val differ = transactionDiffer(time) _ diff --git a/node/src/test/scala/com/wavesplatform/db/WithState.scala b/node/src/test/scala/com/wavesplatform/db/WithState.scala index 42bba1cb503..f80301d13b4 100644 --- a/node/src/test/scala/com/wavesplatform/db/WithState.scala +++ b/node/src/test/scala/com/wavesplatform/db/WithState.scala @@ -12,8 +12,7 @@ import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.events.BlockchainUpdateTriggers import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.features.BlockchainFeatures.LightNode -import com.wavesplatform.history.SnapshotOps.TransactionStateSnapshotExt -import com.wavesplatform.history.{Domain, SnapshotOps} +import com.wavesplatform.history.Domain import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lagonaki.mocks.TestBlock.BlockWithSigner import com.wavesplatform.lang.ValidationError @@ -24,8 +23,10 @@ import com.wavesplatform.settings.{TestFunctionalitySettings as TFS, *} import com.wavesplatform.state.diffs.{BlockDiffer, ENOUGH_AMT} import com.wavesplatform.state.reader.SnapshotBlockchain import com.wavesplatform.state.utils.TestRocksDB -import com.wavesplatform.state.{Blockchain, BlockchainUpdaterImpl, Diff, NgState, Portfolio, StateSnapshot, TxStateSnapshotHashBuilder} +import com.wavesplatform.state.{Blockchain, BlockchainUpdaterImpl, NgState, StateSnapshot, TxStateSnapshotHashBuilder} import com.wavesplatform.test.* +import com.wavesplatform.transaction.Asset.Waves +import com.wavesplatform.transaction.TxHelpers.defaultAddress import com.wavesplatform.transaction.smart.script.trace.TracedResult import com.wavesplatform.transaction.{BlockchainUpdater, GenesisTransaction, Transaction, TxHelpers} import com.wavesplatform.{NTPTime, TestHelpers} @@ -112,7 +113,7 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit fs: FunctionalitySettings = TFS.Enabled, enableExecutionLog: Boolean = false )( - assertion: Either[ValidationError, Diff] => Unit + assertion: Either[ValidationError, StateSnapshot] => Unit ): Unit = withTestState(fs) { (bcu, state) => assertDiffEi(preconditions, block, bcu, state, enableExecutionLog)(assertion) } @@ -124,7 +125,7 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit state: RocksDBWriter, enableExecutionLog: Boolean )( - assertion: Either[ValidationError, Diff] => Unit + assertion: Either[ValidationError, StateSnapshot] => Unit ): Unit = { def differ(blockchain: Blockchain, b: Block) = BlockDiffer.fromBlock( @@ -150,8 +151,11 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit preconditionBlock ) } - val totalDiff1 = blockWithComputedStateHash(block.block, block.signer, bcu).resultE.flatMap(differ(state, _)) - assertion(totalDiff1.map(_.snapshot.toDiff(state))) + val snapshot = + blockWithComputedStateHash(block.block, block.signer, bcu).resultE + .flatMap(differ(state, _)) + .map(_.snapshot) + assertion(snapshot) } def assertDiffEiTraced( @@ -160,7 +164,7 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit fs: FunctionalitySettings = TFS.Enabled, enableExecutionLog: Boolean = false )( - assertion: TracedResult[ValidationError, Diff] => Unit + assertion: TracedResult[ValidationError, StateSnapshot] => Unit ): Unit = withTestState(fs) { (bcu, state) => def getCompBlockchain(blockchain: Blockchain) = { val reward = if (blockchain.height > 0) bcu.computeNextReward else None @@ -196,17 +200,17 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit ) } - val totalDiff1 = + val snapshot1 = (blockWithComputedStateHash(block.block, block.signer, bcu) match { case right @ TracedResult(Right(_), _, _) => right.copy(trace = Nil) case err => err }).flatMap(differ(state, state.lastBlock, _)) - assertion(totalDiff1.map(_.snapshot.toDiff(state))) + assertion(snapshot1.map(_.snapshot)) } private def assertDiffAndState(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, fs: FunctionalitySettings, withNg: Boolean)( - assertion: (Diff, Blockchain) => Unit + assertion: (StateSnapshot, Blockchain) => Unit ): Unit = withTestState(fs) { (bcu, state) => def getCompBlockchain(blockchain: Blockchain) = if (withNg && fs.preActivatedFeatures.get(BlockchainFeatures.BlockReward.id).exists(_ <= blockchain.height)) { @@ -226,8 +230,8 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit preconditions.foldLeft[Option[Block]](None) { (prevBlock, curBlock) => val preconditionBlock = blockWithComputedStateHash(curBlock.block, curBlock.signer, bcu).resultE.explicitGet() - val BlockDiffer.Result(diff, fees, totalFee, _, _, computedStateHash) = differ(state, prevBlock, preconditionBlock).explicitGet() - state.append(diff, fees, totalFee, None, preconditionBlock.header.generationSignature, computedStateHash, preconditionBlock) + val BlockDiffer.Result(snapshot, fees, totalFee, _, _, computedStateHash) = differ(state, prevBlock, preconditionBlock).explicitGet() + state.append(snapshot, fees, totalFee, None, preconditionBlock.header.generationSignature, computedStateHash, preconditionBlock) Some(preconditionBlock) } @@ -245,22 +249,19 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit checkedBlock.header.generationSignature, Map() ) - val cb = SnapshotBlockchain(state, ngState) - val diff = snapshot.toDiff(state) - assertion(diff, cb) - + val cb = SnapshotBlockchain(state, ngState) + assertion(snapshot, cb) state.append(snapshot, fees, totalFee, None, checkedBlock.header.generationSignature, computedStateHash, checkedBlock) - - assertion(diff, state) + assertion(snapshot, state) } def assertNgDiffState(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, fs: FunctionalitySettings = TFS.Enabled)( - assertion: (Diff, Blockchain) => Unit + assertion: (StateSnapshot, Blockchain) => Unit ): Unit = assertDiffAndState(preconditions, block, fs, withNg = true)(assertion) def assertDiffAndState(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, fs: FunctionalitySettings = TFS.Enabled)( - assertion: (Diff, Blockchain) => Unit + assertion: (StateSnapshot, Blockchain) => Unit ): Unit = assertDiffAndState(preconditions, block, fs, withNg = false)(assertion) @@ -288,9 +289,8 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit val checkedBlock = blockWithComputedStateHash(block.block, block.signer, bcu).resultE.explicitGet() differ(state, checkedBlock).map { result => - val snapshot = SnapshotOps.fromDiff(result.snapshot.toDiff(state), state).explicitGet() state.append( - snapshot, + result.snapshot, result.carry, result.totalFee, None, @@ -302,11 +302,15 @@ trait WithState extends BeforeAndAfterAll with DBCacheSettings with Matchers wit }) } - def assertBalanceInvariant(diff: Diff): Unit = { - val portfolioDiff = diff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - portfolioDiff.balance shouldBe 0 - portfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - all(portfolioDiff.assets.values) shouldBe 0 + def assertBalanceInvariant(snapshot: StateSnapshot, db: RocksDBWriter, rewardAndFee: Long = 0): Unit = { + snapshot.balances.toSeq + .map { + case ((`defaultAddress`, Waves), balance) => Waves -> (balance - db.balance(defaultAddress, Waves) - rewardAndFee) + case ((address, asset), balance) => asset -> (balance - db.balance(address, asset)) + } + .groupMap(_._1)(_._2) + .foreach { case (_, balances) => balances.sum shouldBe 0 } + snapshot.leaseBalances.foreach { case (address, balance) => balance shouldBe db.leaseBalance(address) } } def assertLeft(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, fs: FunctionalitySettings = TFS.Enabled)(errorMessage: String): Unit = diff --git a/node/src/test/scala/com/wavesplatform/features/RideV6FailRejectTest.scala b/node/src/test/scala/com/wavesplatform/features/RideV6FailRejectTest.scala index 53d9a623e2d..a3d1f9edf93 100644 --- a/node/src/test/scala/com/wavesplatform/features/RideV6FailRejectTest.scala +++ b/node/src/test/scala/com/wavesplatform/features/RideV6FailRejectTest.scala @@ -888,8 +888,8 @@ class RideV6FailRejectTest extends FreeSpec with WithDomain with OptionValues wi def failTxTest(invoke: Transaction): Unit = { val complexity = ContractLimits.FailFreeInvokeComplexity + 1 test(complexity) { d => - val diff = d.createDiffE(invoke).value - val (_, scriptResult) = diff.scriptResults.headOption.value + val snapshot = d.createDiffE(invoke).value + val (_, scriptResult) = snapshot.scriptResults.headOption.value scriptResult.error.value.text should include(testCase.rejectError) d.appendBlock(invoke) diff --git a/node/src/test/scala/com/wavesplatform/history/BlockRewardSpec.scala b/node/src/test/scala/com/wavesplatform/history/BlockRewardSpec.scala index 70a25d1eae6..1ace799fb1e 100644 --- a/node/src/test/scala/com/wavesplatform/history/BlockRewardSpec.scala +++ b/node/src/test/scala/com/wavesplatform/history/BlockRewardSpec.scala @@ -268,8 +268,8 @@ class BlockRewardSpec extends FreeSpec with WithDomain { "when NG state is empty" in forAll(ngEmptyScenario) { case (miner1, miner2, b2s, b3, m3s) => withDomain(rewardSettings) { d => b2s.foldLeft[Option[Block]](None) { (prevBlock, curBlock) => - val BlockDiffer.Result(diff, carryFee, totalFee, _, _, computedStateHash) = differ(d.rocksDBWriter, prevBlock, curBlock) - d.rocksDBWriter.append(diff, carryFee, totalFee, None, curBlock.header.generationSignature, computedStateHash, curBlock) + val BlockDiffer.Result(snapshot, carryFee, totalFee, _, _, computedStateHash) = differ(d.rocksDBWriter, prevBlock, curBlock) + d.rocksDBWriter.append(snapshot, carryFee, totalFee, None, curBlock.header.generationSignature, computedStateHash, curBlock) Some(curBlock) } diff --git a/node/src/test/scala/com/wavesplatform/history/Domain.scala b/node/src/test/scala/com/wavesplatform/history/Domain.scala index 8ddd0a031d6..f4995c52392 100644 --- a/node/src/test/scala/com/wavesplatform/history/Domain.scala +++ b/node/src/test/scala/com/wavesplatform/history/Domain.scala @@ -15,7 +15,6 @@ import com.wavesplatform.database.{DBExt, Keys, RDB, RocksDBWriter} import com.wavesplatform.events.BlockchainUpdateTriggers import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.features.BlockchainFeatures.{BlockV5, LightNode, RideV6} -import com.wavesplatform.history.SnapshotOps.TransactionStateSnapshotExt import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.script.Script @@ -60,15 +59,14 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri val posSelector: PoSSelector = PoSSelector(blockchainUpdater, None) - val transactionDiffer: Transaction => TracedResult[ValidationError, Diff] = - TransactionDiffer(blockchain.lastBlockTimestamp, System.currentTimeMillis())(blockchain, _).map(_.toDiff(blockchain)) + val transactionDiffer: Transaction => TracedResult[ValidationError, StateSnapshot] = + TransactionDiffer(blockchain.lastBlockTimestamp, System.currentTimeMillis())(blockchain, _) - val transactionDifferWithLog: Transaction => TracedResult[ValidationError, Diff] = + val transactionDifferWithLog: Transaction => TracedResult[ValidationError, StateSnapshot] = TransactionDiffer(blockchain.lastBlockTimestamp, System.currentTimeMillis(), enableExecutionLog = true)(blockchain, _) - .map(_.toDiff(blockchain)) - def createDiffE(tx: Transaction): Either[ValidationError, Diff] = transactionDiffer(tx).resultE - def createDiff(tx: Transaction): Diff = createDiffE(tx).explicitGet() + def createDiffE(tx: Transaction): Either[ValidationError, StateSnapshot] = transactionDiffer(tx).resultE + def createDiff(tx: Transaction): StateSnapshot = createDiffE(tx).explicitGet() lazy val utxPool: UtxPoolImpl = new UtxPoolImpl(SystemTime, blockchain, settings.utxSettings, settings.maxTxErrorLogSize, settings.minerSettings.enable) @@ -123,7 +121,7 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri def commonTransactionsApi(challenger: Option[BlockChallenger]): CommonTransactionsApi = CommonTransactionsApi( - blockchainUpdater.bestLiquidSnapshot.map(diff => Height(blockchainUpdater.height) -> diff), + blockchainUpdater.bestLiquidSnapshot.map(Height(blockchainUpdater.height) -> _), rdb, blockchain, utxPool, @@ -175,8 +173,8 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri .getOrElse(TestBlock.create(Nil).block) } - def liquidDiff: Diff = - blockchainUpdater.bestLiquidSnapshot.orEmpty.toDiff(rocksDBWriter) + def liquidSnapshot: StateSnapshot = + blockchainUpdater.bestLiquidSnapshot.orEmpty def microBlocks: Vector[MicroBlock] = blockchain.microblockIds.reverseIterator.flatMap(blockchain.microBlock).to(Vector) @@ -211,7 +209,7 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri AddressTransactions .allAddressTransactions( rdb, - blockchainUpdater.bestLiquidSnapshot.map(diff => Height(blockchainUpdater.height) -> diff), + blockchainUpdater.bestLiquidSnapshot.map(Height(blockchainUpdater.height) -> _), address, None, Set.empty, @@ -259,7 +257,7 @@ case class Domain(rdb: RDB, blockchainUpdater: BlockchainUpdaterImpl, rocksDBWri def appendAndAssertFailed(tx: Transaction, message: String): Block = { appendBlock(tx) assert(!blockchain.transactionSucceeded(tx.id()), s"should fail: $tx") - liquidDiff.errorMessage(tx.id()).get.text should include(message) + liquidSnapshot.errorMessage(tx.id()).get.text should include(message) lastBlock } diff --git a/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala b/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala deleted file mode 100644 index 185f5ad708a..00000000000 --- a/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala +++ /dev/null @@ -1,109 +0,0 @@ -package com.wavesplatform.history - -import cats.data.Ior -import com.wavesplatform.account.Address -import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.lang.ValidationError -import com.wavesplatform.protobuf.ByteStringExt -import com.wavesplatform.state.* -import com.wavesplatform.state.reader.LeaseDetails -import com.wavesplatform.transaction.Asset.IssuedAsset - -import scala.collection.immutable.VectorMap - -object SnapshotOps { - implicit class TransactionStateSnapshotExt(val s: StateSnapshot) extends AnyVal { - def toDiff(blockchain: Blockchain): Diff = - Diff.withTransactions( - s.transactions.values.toVector, - portfolios(blockchain), - issuedAssets, - updatedAssets, - s.aliases, - orderFills(blockchain), - s.leaseStates.map(l => (l._1, LeaseDetails(l._2.sender, l._2.recipient, l._2.amount, l._2.status, ByteStr.empty, 0))), - s.accountScripts, - s.assetScripts.view.mapValues(Some(_)).toMap, - s.accountData, - s.sponsorships, - scriptsRun = 0, - s.scriptsComplexity, - s.scriptResults, - s.ethereumTransactionMeta - ) - - private def portfolios(blockchain: Blockchain): Map[Address, Portfolio] = - Portfolio.combine(balancePortfolios(blockchain), leasePortfolios(blockchain)).explicitGet() - - private def balancePortfolios(blockchain: Blockchain): Map[Address, Portfolio] = - s.balances - .foldLeft(Map[Address, Portfolio]()) { case (portfolios, ((address, asset), balance)) => - val balanceDiff = balance - blockchain.balance(address, asset) - if (balanceDiff != 0) { - val portfolio = Portfolio.build(asset, balanceDiff) - Portfolio.combine(portfolios, Map(address -> portfolio)).explicitGet() - } else - portfolios - } - - private def leasePortfolios(blockchain: Blockchain): Map[Address, Portfolio] = - s.leaseBalances - .map { case (address, current) => - val init = blockchain.leaseBalance(address) - address -> Portfolio(lease = LeaseBalance(in = current.in - init.in, out = current.out - init.out)) - } - - private def issuedAssets: VectorMap[IssuedAsset, NewAssetInfo] = - VectorMap[IssuedAsset, NewAssetInfo]() ++ s.assetStatics.map { case (asset, pbStatic) => - val static = AssetStaticInfo( - asset.id, - pbStatic.sourceTransactionId.toTxId, - pbStatic.issuerPublicKey.toPublicKey, - pbStatic.decimals, - pbStatic.nft - ) - asset -> NewAssetInfo(static, s.assetNamesAndDescriptions(asset), s.assetVolumes(asset)) - } - - private def updatedAssets: Map[IssuedAsset, Ior[AssetInfo, AssetVolumeInfo]] = - (s.assetVolumes.keySet ++ s.assetNamesAndDescriptions.keySet) - .filterNot(issuedAssets.contains) - .map { asset => - val info = - (s.assetNamesAndDescriptions.get(asset), s.assetVolumes.get(asset)) match { - case (Some(dynamic), Some(volume)) => Ior.Both(dynamic, volume) - case (Some(dynamic), None) => Ior.Left(dynamic) - case (None, Some(volume)) => Ior.Right(volume) - case _ => ??? - } - asset -> info - } - .toMap - - private def orderFills(blockchain: Blockchain): Map[ByteStr, VolumeAndFee] = - s.orderFills.map { case (orderId, info) => - val init = blockchain.filledVolumeAndFee(orderId) - orderId -> VolumeAndFee(info.volume - init.volume, info.fee - init.fee) - } - } - - def fromDiff(diff: Diff, blockchain: Blockchain): Either[ValidationError, StateSnapshot] = - StateSnapshot.build( - blockchain, - diff.portfolios, - diff.orderFills, - diff.issuedAssets, - diff.updatedAssets, - diff.assetScripts.collect { case (asset, Some(info)) => (asset, info) }, - diff.sponsorship, - diff.leaseState.map(l => (l._1, LeaseSnapshot(l._2.sender, l._2.recipient, l._2.amount, l._2.status))), - diff.aliases, - diff.accountData, - diff.scripts, - diff.scriptResults, - diff.ethereumTransactionMeta, - diff.scriptsComplexity, - VectorMap() ++ diff.transactions.map(info => info.transaction.id() -> info).toMap - ) -} diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala index ad6aca80e59..fcd39a0b456 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala @@ -421,7 +421,7 @@ class TransactionsRouteSpec val differ = blockchain.stub.transactionDiffer().andThen(_.resultE.explicitGet()) val transaction = EthTxGenerator.generateEthTransfer(TxHelpers.defaultEthSigner, TxHelpers.secondAddress, 10, Waves) - val diff = differ(transaction) + val snapshot = differ(transaction) val transactionsApi = stub[CommonTransactionsApi] (transactionsApi.transactionById _) .when(transaction.id()) @@ -432,8 +432,8 @@ class TransactionsRouteSpec transaction, Status.Succeeded, 15L, - diff.ethereumTransactionMeta.values.headOption, - diff.scriptResults.values.headOption + snapshot.ethereumTransactionMeta.values.headOption, + snapshot.scriptResults.values.headOption ) ) ) @@ -479,7 +479,7 @@ class TransactionsRouteSpec val differ = blockchain.stub.transactionDiffer().andThen(_.resultE.explicitGet()) val transaction = EthTxGenerator.generateEthInvoke(TxHelpers.defaultEthSigner, TxHelpers.secondAddress, "test", Nil, Nil) - val diff = differ(transaction) + val snapshot = differ(transaction) val transactionsApi = stub[CommonTransactionsApi] (transactionsApi.transactionById _) .when(transaction.id()) @@ -490,8 +490,8 @@ class TransactionsRouteSpec transaction, Status.Succeeded, 15L, - diff.ethereumTransactionMeta.values.headOption, - diff.scriptResults.values.headOption + snapshot.ethereumTransactionMeta.values.headOption, + snapshot.scriptResults.values.headOption ) ) ) diff --git a/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala b/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala index 1137945f7b1..2f8f9d41700 100644 --- a/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala @@ -102,7 +102,7 @@ class BlockWithMaxBaseTargetTest extends FreeSpec with WithNewDBForEachTest with case _: SecurityException => Task.unit } - Await.result(blockAppendTask.runToFuture(scheduler), Duration.Inf) + Await.result(blockAppendTask.runToFuture(scheduler), 1.minute) signal.tryAcquire(10, TimeUnit.SECONDS) diff --git a/node/src/test/scala/com/wavesplatform/state/Diff.scala b/node/src/test/scala/com/wavesplatform/state/Diff.scala deleted file mode 100755 index d71987f26b0..00000000000 --- a/node/src/test/scala/com/wavesplatform/state/Diff.scala +++ /dev/null @@ -1,182 +0,0 @@ -package com.wavesplatform.state - -import cats.data.Ior -import cats.implicits.{catsSyntaxEitherId, catsSyntaxSemigroup, toTraverseOps} -import com.google.common.hash.{BloomFilter, Funnels} -import com.wavesplatform.account.{Address, Alias, PublicKey} -import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.database.protobuf.EthereumTransactionMeta -import com.wavesplatform.state.reader.LeaseDetails -import com.wavesplatform.transaction.Asset.IssuedAsset -import com.wavesplatform.transaction.Transaction - -import scala.collection.immutable.VectorMap -import scala.util.chaining.* - -case class Diff( - transactions: Vector[NewTransactionInfo], - portfolios: Map[Address, Portfolio], - issuedAssets: VectorMap[IssuedAsset, NewAssetInfo], - updatedAssets: Map[IssuedAsset, Ior[AssetInfo, AssetVolumeInfo]], - aliases: Map[Alias, Address], - orderFills: Map[ByteStr, VolumeAndFee], - leaseState: Map[ByteStr, LeaseDetails], - scripts: Map[PublicKey, Option[AccountScriptInfo]], - assetScripts: Map[IssuedAsset, Option[AssetScriptInfo]], - accountData: Map[Address, Map[String, DataEntry[?]]], - sponsorship: Map[IssuedAsset, Sponsorship], - scriptsRun: Int, - scriptsComplexity: Long, - scriptResults: Map[ByteStr, InvokeScriptResult], - ethereumTransactionMeta: Map[ByteStr, EthereumTransactionMeta], - transactionFilter: Option[BloomFilter[Array[Byte]]] -) { - def containsTransaction(txId: ByteStr): Boolean = - transactions.nonEmpty && transactionFilter.exists(_.mightContain(txId.arr)) && transactions.exists(_.transaction.id() == txId) - - def transaction(txId: ByteStr): Option[NewTransactionInfo] = - if (transactions.nonEmpty && transactionFilter.exists(_.mightContain(txId.arr))) - transactions.find(_.transaction.id() == txId) - else None - - def combineF(newer: Diff): Either[String, Diff] = - for { - portfolios <- Portfolio.combine(portfolios, newer.portfolios) - orderFills <- { - val combinedOrders = - orderFills.toSeq - .traverse { case kv @ (orderId, value) => - newer.orderFills.get(orderId).fold(kv.asRight[String])(value.combineE(_).map(orderId -> _)) - } - .map(_.toMap) - combinedOrders.map(co => co ++ newer.orderFills.filterNot { case (id, _) => co.contains(id) }) - } - newTransactions = if (transactions.isEmpty) newer.transactions else transactions ++ newer.transactions - newFilter = transactionFilter match { - case Some(bf) => - newer.transactions.foreach(nti => bf.put(nti.transaction.id().arr)) - Some(bf) - case None => - newer.transactionFilter - } - } yield Diff( - transactions = newTransactions, - portfolios = portfolios, - issuedAssets = issuedAssets ++ newer.issuedAssets, - updatedAssets = updatedAssets |+| newer.updatedAssets, - aliases = aliases ++ newer.aliases, - orderFills = orderFills, - leaseState = leaseState ++ newer.leaseState, - scripts = scripts ++ newer.scripts, - assetScripts = assetScripts ++ newer.assetScripts, - accountData = Diff.combine(accountData, newer.accountData), - sponsorship = sponsorship.combine(newer.sponsorship), - scriptsRun = scriptsRun + newer.scriptsRun, - scriptResults = scriptResults.combine(newer.scriptResults), - scriptsComplexity = scriptsComplexity + newer.scriptsComplexity, - ethereumTransactionMeta = ethereumTransactionMeta ++ newer.ethereumTransactionMeta, - transactionFilter = newFilter - ) -} - -object Diff { - def apply( - portfolios: Map[Address, Portfolio] = Map.empty, - issuedAssets: VectorMap[IssuedAsset, NewAssetInfo] = VectorMap.empty, - updatedAssets: Map[IssuedAsset, Ior[AssetInfo, AssetVolumeInfo]] = Map.empty, - aliases: Map[Alias, Address] = Map.empty, - orderFills: Map[ByteStr, VolumeAndFee] = Map.empty, - leaseState: Map[ByteStr, LeaseDetails] = Map.empty, - scripts: Map[PublicKey, Option[AccountScriptInfo]] = Map.empty, - assetScripts: Map[IssuedAsset, Option[AssetScriptInfo]] = Map.empty, - accountData: Map[Address, Map[String, DataEntry[?]]] = Map.empty, - sponsorship: Map[IssuedAsset, Sponsorship] = Map.empty, - scriptsRun: Int = 0, - scriptsComplexity: Long = 0, - scriptResults: Map[ByteStr, InvokeScriptResult] = Map.empty, - ethereumTransactionMeta: Map[ByteStr, EthereumTransactionMeta] = Map.empty - ): Diff = - new Diff( - Vector.empty, - portfolios, - issuedAssets, - updatedAssets, - aliases, - orderFills, - leaseState, - scripts, - assetScripts, - accountData, - sponsorship, - scriptsRun, - scriptsComplexity, - scriptResults, - ethereumTransactionMeta, - None - ) - - def withTransactions( - nti: Vector[NewTransactionInfo], - portfolios: Map[Address, Portfolio] = Map.empty, - issuedAssets: VectorMap[IssuedAsset, NewAssetInfo] = VectorMap.empty, - updatedAssets: Map[IssuedAsset, Ior[AssetInfo, AssetVolumeInfo]] = Map.empty, - aliases: Map[Alias, Address] = Map.empty, - orderFills: Map[ByteStr, VolumeAndFee] = Map.empty, - leaseState: Map[ByteStr, LeaseDetails] = Map.empty, - scripts: Map[PublicKey, Option[AccountScriptInfo]] = Map.empty, - assetScripts: Map[IssuedAsset, Option[AssetScriptInfo]] = Map.empty, - accountData: Map[Address, Map[String, DataEntry[?]]] = Map.empty, - sponsorship: Map[IssuedAsset, Sponsorship] = Map.empty, - scriptsRun: Int = 0, - scriptsComplexity: Long = 0, - scriptResults: Map[ByteStr, InvokeScriptResult] = Map.empty, - ethereumTransactionMeta: Map[ByteStr, EthereumTransactionMeta] = Map.empty - ): Diff = - new Diff( - nti, - portfolios, - issuedAssets, - updatedAssets, - aliases, - orderFills, - leaseState, - scripts, - assetScripts, - accountData, - sponsorship, - scriptsRun, - scriptsComplexity, - scriptResults, - ethereumTransactionMeta, - mkFilterForTransactions(nti.map(_.transaction)*) - ) - - val empty: Diff = Diff() - - private def combine[K, IK, IV](first: Map[K, Map[IK, IV]], second: Map[K, Map[IK, IV]]): Map[K, Map[IK, IV]] = { - if (first.isEmpty) { - second - } else { - first ++ second.map { case (k, innerMap) => - k -> first.get(k).fold(innerMap)(_ ++ innerMap) - } - } - } - - private def mkFilter() = - BloomFilter.create[Array[Byte]](Funnels.byteArrayFunnel(), 10000, 0.01f) - - private def mkFilterForTransactions(tx: Transaction*) = - Some( - mkFilter().tap(bf => - tx.foreach { t => - bf.put(t.id().arr) - } - ) - ) - - implicit class DiffExt(private val d: Diff) extends AnyVal { - def errorMessage(txId: ByteStr): Option[InvokeScriptResult.ErrorMessage] = - d.scriptResults.get(txId).flatMap(_.error) - } -} diff --git a/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala b/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala index 807d67a5ffd..dc2e309d20a 100644 --- a/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/LightNodeTest.scala @@ -138,7 +138,15 @@ class LightNodeTest extends PropSpec with WithDomain { ) val appender = - ExtensionAppender(d.blockchain, d.utxPool, d.posSelector, TestTime(), InvalidBlockStorage.NoOp, PeerDatabase.NoOp, Scheduler.global)( + ExtensionAppender( + d.blockchain, + d.utxPool, + d.posSelector, + TestTime(extensionBlocks.blocks.last.header.timestamp), + InvalidBlockStorage.NoOp, + PeerDatabase.NoOp, + Scheduler.global + )( null, _ ) @@ -162,7 +170,7 @@ class LightNodeTest extends PropSpec with WithDomain { val challengingBlock = d.createChallengingBlock(challengingMiner, invalidBlock, strictTime = true) val txSnapshots = getTxSnapshots(d, challengingBlock) - val appender = BlockAppender(d.blockchainUpdater, TestTime(), d.utxPool, d.posSelector, Scheduler.global) _ + val appender = BlockAppender(d.blockchainUpdater, TestTime(challengingBlock.header.timestamp), d.utxPool, d.posSelector, Scheduler.global) _ appender(challengingBlock, Some(BlockSnapshot(challengingBlock.id(), txSnapshots))).runSyncUnsafe() shouldBe Right( Applied(Seq.empty, d.blockchain.score) diff --git a/node/src/test/scala/com/wavesplatform/state/TransactionsByAddressSpec.scala b/node/src/test/scala/com/wavesplatform/state/TransactionsByAddressSpec.scala index 39ea66c7862..53dcb4c89e9 100644 --- a/node/src/test/scala/com/wavesplatform/state/TransactionsByAddressSpec.scala +++ b/node/src/test/scala/com/wavesplatform/state/TransactionsByAddressSpec.scala @@ -21,7 +21,6 @@ import java.util.concurrent.locks.ReentrantLock import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.{Await, Future} import scala.concurrent.duration.* -import scala.concurrent.duration.Duration.Inf class TransactionsByAddressSpec extends FreeSpec with BlockGen with WithDomain { def transfers(sender: KeyPair, rs: AddressOrAlias, amount: Long): Seq[TransferTransaction] = @@ -122,7 +121,7 @@ class TransactionsByAddressSpec extends FreeSpec with BlockGen with WithDomain { val txs = Future { d.addressTransactions(defaultAddress).map(_._2.tpe) } d.blockchain.bestLiquidSnapshot.synchronized(d.appendKeyBlock()) startRead.unlock() - Await.result(txs, Inf).map(_.tpe) shouldBe List(TransactionType.Issue) + Await.result(txs, 1.minute).map(_.tpe) shouldBe List(TransactionType.Issue) } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/AssetTransactionsDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/AssetTransactionsDiffTest.scala index 8786f5d65ef..ef12359b942 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/AssetTransactionsDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/AssetTransactionsDiffTest.scala @@ -24,7 +24,8 @@ import com.wavesplatform.state.diffs.TransactionDiffer.TransactionValidationErro import com.wavesplatform.state.diffs.smart.smartEnabledFS import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.* -import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} +import com.wavesplatform.transaction.TxHelpers.defaultAddress import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.transaction.assets.* import com.wavesplatform.transaction.transfer.* @@ -42,7 +43,7 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers val issue = TxHelpers.issue(master, 100, reissuable = isReissuable, version = TxVersion.V1) val asset = IssuedAsset(issue.id()) - val reissue = TxHelpers.reissue(asset, master, 50, version = TxVersion.V1) + val reissue = TxHelpers.reissue(asset, master, 50, version = TxVersion.V1, fee = 1.waves) val burn = TxHelpers.burn(asset, 10, master, version = TxVersion.V1) ((genesis, issue), (reissue, burn)) @@ -50,15 +51,27 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers property("Issue+Reissue+Burn do not break waves invariant and updates state") { val ((gen, issue), (reissue, burn)) = issueReissueBurnTxs(isReissuable = true) - assertDiffAndState(Seq(TestBlock.create(Seq(gen, issue))), TestBlock.create(Seq(reissue, burn))) { case (blockDiff, newState) => - val totalPortfolioDiff = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets shouldBe Map(reissue.asset -> (reissue.quantity.value - burn.quantity.value)) - - val totalAssetVolume = issue.quantity.value + reissue.quantity.value - burn.quantity.value - newState.balance(issue.sender.toAddress, reissue.asset) shouldEqual totalAssetVolume + withDomain(RideV3) { d => + d.appendBlock(gen) + d.appendBlock(issue) + d.appendBlock(reissue, burn) + val assetQuantityDiff = reissue.quantity.value - burn.quantity.value + d.liquidSnapshot.balances.toSeq + .map { + case ((`defaultAddress`, Waves), amount) => + val carryFee = (-issue.fee.value + reissue.fee.value + burn.fee.value) / 5 * 3 + Waves -> (amount - d.rocksDBWriter.balance(defaultAddress, Waves) + carryFee) + case ((address, asset), amount) => + asset -> (amount - d.rocksDBWriter.balance(address, asset)) + } + .groupMap(_._1)(_._2) + .foreach { + case (asset, Seq(balanceDiff)) if asset == reissue.asset => balanceDiff shouldBe assetQuantityDiff + case (_, balanceDiff) => balanceDiff.sum shouldBe 0 + } + val resultQuantity = issue.quantity.value + assetQuantityDiff + d.liquidSnapshot.assetVolumes.view.mapValues(_.volume).toMap shouldBe Map(reissue.asset -> resultQuantity) + d.balance(issue.sender.toAddress, reissue.asset) shouldEqual resultQuantity } } @@ -205,7 +218,7 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers private def createScript(code: String, version: StdLibVersion) = { val Parsed.Success(expr, _) = Parser.parseExpr(code).get - ExprScript(version, ExpressionCompiler(compilerContext(version, Expression, isAssetScript = false), expr).explicitGet()._1).explicitGet() + ExprScript(version, ExpressionCompiler(compilerContext(version, Expression, isAssetScript = false), version, expr).explicitGet()._1).explicitGet() } def genesisIssueTransferReissue( @@ -249,7 +262,7 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers Height @@ 2 ) ) - blockDiff.transaction(issue.id()) shouldBe defined + blockDiff.transactions.get(issue.id()) shouldBe defined newState.transactionInfo(issue.id()).isDefined shouldBe true } } @@ -257,8 +270,8 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers property("Can transfer when script evaluates to TRUE") { val (gen, issue, transfer, _, _) = genesisIssueTransferReissue("true") assertDiffAndState(Seq(TestBlock.create(gen)), TestBlock.create(Seq(issue, transfer)), smartEnabledFS) { case (blockDiff, newState) => - val totalPortfolioDiff = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.assets(IssuedAsset(issue.id())) shouldEqual issue.quantity.value + val asset = IssuedAsset(issue.id()) + blockDiff.balances.collect { case ((_, `asset`), quantity) => quantity }.sum shouldEqual issue.quantity.value newState.balance(newState.resolveAlias(transfer.recipient).explicitGet(), IssuedAsset(issue.id())) shouldEqual transfer.amount.value } } @@ -320,12 +333,7 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers ) assertDiffEi(blocks, TestBlock.create(Seq(update), Block.ProtoBlockVersion), assetInfoUpdateEnabled) { ei => - val info = ei - .explicitGet() - .updatedAssets(update.assetId) - .left - .get - + val info = ei.explicitGet().assetNamesAndDescriptions(update.assetId) info.name.toStringUtf8 shouldEqual update.name info.description.toStringUtf8 shouldEqual update.description } @@ -403,8 +411,8 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers val (genesis1, issue1, _, _, _) = genesisIssueTransferReissue(exprV4WithComplexityBetween3000And4000, V4) assertDiffAndState(Seq(TestBlock.create(genesis1)), TestBlock.create(Seq(issue1)), rideV4Activated) { case (blockDiff, _) => - val totalPortfolioDiff = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.assets(IssuedAsset(issue1.id())) shouldEqual issue1.quantity.value + val asset = IssuedAsset(issue1.id()) + blockDiff.balances.collect { case ((_, `asset`), quantity) => quantity }.sum shouldEqual issue1.quantity.value } val (genesis2, issue2, _, _, _) = genesisIssueTransferReissue(exprV4WithComplexityAbove4000, V4) @@ -467,7 +475,7 @@ class AssetTransactionsDiffTest extends PropSpec with BlocksTransactionsHelpers db.appendBlock(preparingTxs*) val tx = scriptedTx() db.appendBlock(tx) - db.liquidDiff.errorMessage(tx.id()) shouldBe None + db.liquidSnapshot.errorMessage(tx.id()) shouldBe None } withDomain(domainSettingsWithFS(settings(checkNegative = true))) { db => diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/BalanceDiffValidationTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/BalanceDiffValidationTest.scala index dc64f34548e..3106324e0dd 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/BalanceDiffValidationTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/BalanceDiffValidationTest.scala @@ -40,8 +40,8 @@ class BalanceDiffValidationTest extends PropSpec with WithState { Seq(TestBlock.create(Seq(genesis, masterTransfersToAlice, aliceLeasesToBob, masterLeasesToAlice))), TestBlock.create(Seq(aliceTransfersMoreThanOwnsMinusLeaseOut)), settings - ) { totalDiffEi => - totalDiffEi.explicitGet() + ) { snapshotEi => + snapshotEi.explicitGet() } } @@ -59,8 +59,8 @@ class BalanceDiffValidationTest extends PropSpec with WithState { ), TestBlock.create(Seq(aliceTransfersMoreThanOwnsMinusLeaseOut)), settings - ) { totalDiffEi => - totalDiffEi should produce("trying to spend leased money") + ) { snapshotEi => + snapshotEi should produce("trying to spend leased money") } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala index d8453155046..1983fbc5e04 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/BlockDifferTest.scala @@ -14,7 +14,7 @@ import com.wavesplatform.mining.{MinerImpl, MiningConstraint} import com.wavesplatform.settings.FunctionalitySettings import com.wavesplatform.state.diffs.BlockDiffer.Result import com.wavesplatform.state.reader.SnapshotBlockchain -import com.wavesplatform.state.{Blockchain, Diff, StateSnapshot, TxStateSnapshotHashBuilder} +import com.wavesplatform.state.{Blockchain, StateSnapshot, TxStateSnapshotHashBuilder} import com.wavesplatform.test.* import com.wavesplatform.test.node.* import com.wavesplatform.transaction.TxValidationError.InvalidStateHash @@ -303,7 +303,7 @@ class BlockDifferTest extends FreeSpec with WithDomain { } } - private def assertDiff(blocks: Seq[BlockWithSigner], ngAtHeight: Int)(assertion: (Diff, Blockchain) => Unit): Unit = { + private def assertDiff(blocks: Seq[BlockWithSigner], ngAtHeight: Int)(assertion: (StateSnapshot, Blockchain) => Unit): Unit = { val fs = FunctionalitySettings( featureCheckBlocksPeriod = ngAtHeight / 2, blocksForFeatureActivation = 1, diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/CreateAliasTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/CreateAliasTransactionDiffTest.scala index 0b8a11fbc91..31483e179a6 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/CreateAliasTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/CreateAliasTransactionDiffTest.scala @@ -2,22 +2,24 @@ package com.wavesplatform.state.diffs import com.wavesplatform.account.Alias import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.db.WithState +import com.wavesplatform.db.WithDomain import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.history.SnapshotOps +import com.wavesplatform.features.BlockchainFeatures.SmartAccounts import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.settings.{FunctionalitySettings, TestFunctionalitySettings} import com.wavesplatform.state.* import com.wavesplatform.state.utils.addressTransactions import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} +import com.wavesplatform.test.DomainPresets.{NG, RideV3, WavesSettingsOps} +import com.wavesplatform.transaction.Asset.Waves +import com.wavesplatform.transaction.TxHelpers.defaultAddress import com.wavesplatform.transaction.assets.IssueTransaction import com.wavesplatform.transaction.lease.LeaseTransaction import com.wavesplatform.transaction.transfer.TransferTransaction import com.wavesplatform.transaction.{Asset, CreateAliasTransaction, GenesisTransaction, TransactionType, TxHelpers, TxVersion} import monix.execution.Scheduler.Implicits.global -class CreateAliasTransactionDiffTest extends PropSpec with WithState { +class CreateAliasTransactionDiffTest extends PropSpec with WithDomain { val fs: FunctionalitySettings = TestFunctionalitySettings.Enabled.copy( @@ -39,16 +41,16 @@ class CreateAliasTransactionDiffTest extends PropSpec with WithState { TxHelpers.createAlias(alias.name, master, fee = fee, version = TxVersion.V1) ) val sameAliasTxs = Seq( - TxHelpers.createAlias(alias.name, master, fee = fee + 1), - TxHelpers.createAlias(alias.name, master, fee = fee + 1, version = TxVersion.V1) + TxHelpers.createAlias(alias.name, master, fee = fee + 100), + TxHelpers.createAlias(alias.name, master, fee = fee + 100, version = TxVersion.V1) ) val sameAliasOtherSenderTxs = Seq( - TxHelpers.createAlias(alias.name, other, fee = fee + 2), - TxHelpers.createAlias(alias.name, other, fee = fee + 2, version = TxVersion.V1) + TxHelpers.createAlias(alias.name, other, fee = fee + 200), + TxHelpers.createAlias(alias.name, other, fee = fee + 200, version = TxVersion.V1) ) val anotherAliasTxs = Seq( - TxHelpers.createAlias(alias2.name, master, fee = fee + 3), - TxHelpers.createAlias(alias2.name, master, fee = fee + 3, version = TxVersion.V1) + TxHelpers.createAlias(alias2.name, master, fee = fee + 300), + TxHelpers.createAlias(alias2.name, master, fee = fee + 300, version = TxVersion.V1) ) for { @@ -61,28 +63,28 @@ class CreateAliasTransactionDiffTest extends PropSpec with WithState { property("can create and resolve aliases preserving waves invariant") { preconditionsAndAliasCreations.foreach { case (gen, aliasTx, _, _, anotherAliasTx) => - assertDiffAndState(Seq(TestBlock.create(Seq(gen, aliasTx))), TestBlock.create(Seq(anotherAliasTx)), fs) { case (blockDiff, newState) => - val totalPortfolioDiff = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - + withDomain(RideV3) { d => + d.appendBlock(gen) + d.appendBlock(aliasTx) + d.appendBlock(anotherAliasTx) + d.liquidSnapshot.balances.collect { + case ((`defaultAddress`, Waves), balance) => + val carryFee = (anotherAliasTx.fee.value - aliasTx.fee.value) / 5 * 3 + balance - d.rocksDBWriter.balance(defaultAddress) + carryFee + case ((address, Waves), balance) => + balance - d.rocksDBWriter.balance(address) + }.sum shouldBe 0 val senderAcc = anotherAliasTx.sender.toAddress - blockDiff.aliases shouldBe Map(anotherAliasTx.alias -> senderAcc) - + d.liquidSnapshot.aliases shouldBe Map(anotherAliasTx.alias -> senderAcc) addressTransactions( rdb, - Some(Height(newState.height + 1) -> SnapshotOps.fromDiff(blockDiff, newState).explicitGet()), + Some(Height(d.blockchain.height + 1) -> d.liquidSnapshot), senderAcc, Set(TransactionType.CreateAlias), None - ).collect { case (_, cat: CreateAliasTransaction) => - cat.alias - }.toSet shouldBe Set( - anotherAliasTx.alias, - aliasTx.alias - ) - newState.resolveAlias(aliasTx.alias) shouldBe Right(senderAcc) - newState.resolveAlias(anotherAliasTx.alias) shouldBe Right(senderAcc) + ).collect { case (_, cat: CreateAliasTransaction) => cat.alias }.toSet shouldBe Set(anotherAliasTx.alias, aliasTx.alias) + d.blockchain.resolveAlias(aliasTx.alias) shouldBe Right(senderAcc) + d.blockchain.resolveAlias(anotherAliasTx.alias) shouldBe Right(senderAcc) } } } @@ -92,7 +94,6 @@ class CreateAliasTransactionDiffTest extends PropSpec with WithState { assertDiffEi(Seq(TestBlock.create(Seq(gen, aliasTx))), TestBlock.create(Seq(sameAliasTx)), fs) { blockDiffEi => blockDiffEi should produce("AlreadyInTheState") } - assertDiffEi(Seq(TestBlock.create(Seq(gen, aliasTx))), TestBlock.create(Seq(sameAliasOtherSenderTx)), fs) { blockDiffEi => blockDiffEi should produce("AlreadyInTheState") } @@ -129,13 +130,13 @@ class CreateAliasTransactionDiffTest extends PropSpec with WithState { property("Can transfer to alias") { preconditionsTransferLease.foreach { case (genesis, issue1, issue2, aliasTx, transfer, _) => - assertDiffAndState(Seq(TestBlock.create(genesis :+ issue1 :+ issue2 :+ aliasTx)), TestBlock.create(Seq(transfer))) { case (blockDiff, _) => + withDomain(NG.addFeatures(SmartAccounts)) { d => + d.appendBlock(genesis*) + d.appendBlock(issue1, issue2, aliasTx) + d.appendBlock(transfer) if (transfer.sender.toAddress != aliasTx.sender.toAddress) { - val recipientPortfolioDiff = blockDiff.portfolios(aliasTx.sender.toAddress) - transfer.assetId match { - case aid @ IssuedAsset(_) => recipientPortfolioDiff shouldBe Portfolio.build(aid, transfer.amount.value) - case Waves => recipientPortfolioDiff shouldBe Portfolio(transfer.amount.value) - } + d.liquidSnapshot.balances((aliasTx.sender.toAddress, transfer.assetId)) shouldBe + transfer.amount.value + d.rocksDBWriter.balance(aliasTx.sender.toAddress, transfer.assetId) } } } @@ -145,8 +146,8 @@ class CreateAliasTransactionDiffTest extends PropSpec with WithState { preconditionsTransferLease.foreach { case (genesis, issue1, issue2, aliasTx, _, lease) => assertDiffEi(Seq(TestBlock.create(genesis :+ issue1 :+ issue2 :+ aliasTx)), TestBlock.create(Seq(lease))) { blockDiffEi => if (lease.sender.toAddress != aliasTx.sender.toAddress) { - val recipientPortfolioDiff = blockDiffEi.explicitGet().portfolios(aliasTx.sender.toAddress) - recipientPortfolioDiff shouldBe Portfolio(0, LeaseBalance(lease.amount.value, 0)) + blockDiffEi.explicitGet().balances.get((aliasTx.sender.toAddress, Waves)) shouldBe None + blockDiffEi.explicitGet().leaseBalances(aliasTx.sender.toAddress) shouldBe LeaseBalance(lease.amount.value, 0) } else { blockDiffEi should produce("Cannot lease to self") } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/DataTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/DataTransactionDiffTest.scala index 4ddaaff042d..3295bf319ed 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/DataTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/DataTransactionDiffTest.scala @@ -2,15 +2,17 @@ package com.wavesplatform.state.diffs import com.wavesplatform.account.KeyPair import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.db.WithState +import com.wavesplatform.db.WithDomain import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lagonaki.mocks.TestBlock.create as block import com.wavesplatform.settings.TestFunctionalitySettings import com.wavesplatform.state.{BinaryDataEntry, BooleanDataEntry, IntegerDataEntry} import com.wavesplatform.test.* +import com.wavesplatform.test.DomainPresets.RideV3 +import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.{GenesisTransaction, TxHelpers} -class DataTransactionDiffTest extends PropSpec with WithState { +class DataTransactionDiffTest extends PropSpec with WithDomain { val fs = TestFunctionalitySettings.Enabled.copy(preActivatedFeatures = Map(BlockchainFeatures.DataTransaction.id -> 0)) @@ -41,34 +43,38 @@ class DataTransactionDiffTest extends PropSpec with WithState { } val (genesisTx, items, txs) = setup - val sender = txs.head.sender - val genesis = block(Seq(genesisTx)) - val blocks = txs.map(tx => block(Seq(tx))) + val sender = txs.head.sender.toAddress val item1 = items.head - assertDiffAndState(Seq(genesis), blocks(0), fs) { - case (totalDiff, state) => - assertBalanceInvariant(totalDiff) - state.balance(sender.toAddress) shouldBe (ENOUGH_AMT - txs(0).fee.value) - state.accountData(sender.toAddress, item1.key) shouldBe Some(item1) + withDomain(RideV3) { d => + d.appendBlock(genesisTx) + d.appendBlock(txs(0)) + d.liquidSnapshot.balances((sender, Waves)) shouldBe (ENOUGH_AMT - txs(0).fee.value) + d.liquidSnapshot.accountData(sender)(item1.key) shouldBe item1 + val carryFee = -txs(0).fee.value * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, carryFee) } val item2 = items(1) - assertDiffAndState(Seq(genesis, blocks(0)), blocks(1), fs) { - case (totalDiff, state) => - assertBalanceInvariant(totalDiff) - state.balance(sender.toAddress) shouldBe (ENOUGH_AMT - txs.take(2).map(_.fee.value).sum) - state.accountData(sender.toAddress, item1.key) shouldBe Some(item1) - state.accountData(sender.toAddress, item2.key) shouldBe Some(item2) + withDomain(RideV3) { d => + d.appendBlock(genesisTx) + d.appendBlock(txs(0), txs(1)) + d.liquidSnapshot.balances((sender, Waves)) shouldBe (ENOUGH_AMT - txs.take(2).map(_.fee.value).sum) + d.liquidSnapshot.accountData(sender)(item1.key) shouldBe item1 + d.liquidSnapshot.accountData(sender)(item2.key) shouldBe item2 + val carryFee = -(txs(0).fee.value + txs(1).fee.value) * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, carryFee) } val item3 = items(2) - assertDiffAndState(Seq(genesis, blocks(0), blocks(1)), blocks(2), fs) { - case (totalDiff, state) => - assertBalanceInvariant(totalDiff) - state.balance(sender.toAddress) shouldBe (ENOUGH_AMT - txs.map(_.fee.value).sum) - state.accountData(sender.toAddress, item1.key) shouldBe Some(item3) - state.accountData(sender.toAddress, item2.key) shouldBe Some(item2) + withDomain(RideV3) { d => + d.appendBlock(genesisTx) + d.appendBlock(txs(0), txs(1), txs(2)) + d.liquidSnapshot.balances((sender, Waves)) shouldBe (ENOUGH_AMT - txs.map(_.fee.value).sum) + d.liquidSnapshot.accountData(sender)(item1.key) shouldBe item3 + d.liquidSnapshot.accountData(sender)(item2.key) shouldBe item2 + val carryFee = -(txs(0).fee.value + txs(1).fee.value + txs(2).fee.value) * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, carryFee) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/EthereumTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/EthereumTransactionDiffTest.scala index fb9831b2464..1c403c22e20 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/EthereumTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/EthereumTransactionDiffTest.scala @@ -4,25 +4,24 @@ import com.wavesplatform.TestValues import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.crypto.EthereumKeyLength -import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.db.WithDomain +import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lang.directives.values.V6 +import com.wavesplatform.lang.v1.ContractLimits.MaxInvokeScriptSizeInBytes import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.settings.RewardsVotingSettings -import com.wavesplatform.state.{Diff, Portfolio} +import com.wavesplatform.state.diffs.TransactionDiffer.TransactionValidationError import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.transaction.Asset.Waves +import com.wavesplatform.transaction.EthTxGenerator.Arg import com.wavesplatform.transaction.EthereumTransaction.Transfer import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.transaction.smart.InvokeScriptTransaction import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment -import com.wavesplatform.transaction.{Asset, EthTxGenerator, EthereumTransaction, TxHelpers} import com.wavesplatform.transaction.utils.EthConverters.* -import EthTxGenerator.Arg -import com.wavesplatform.lang.v1.ContractLimits.MaxInvokeScriptSizeInBytes -import com.wavesplatform.state.diffs.TransactionDiffer.TransactionValidationError +import com.wavesplatform.transaction.{Asset, EthTxGenerator, EthereumTransaction, TxHelpers} import com.wavesplatform.utils.{DiffMatchers, EthEncoding, JsonMatchers} import org.web3j.crypto.{Bip32ECKeyPair, RawTransaction} import play.api.libs.json.Json @@ -93,29 +92,28 @@ class EthereumTransactionDiffTest extends FlatSpec with WithDomain with DiffMatc val recipient = TxHelpers.address(2) val issuer = TxHelpers.signer(3) - val fee = TestValues.fee - val feeDiff = Diff(portfolios = Map(senderKp.toWavesAddress -> Portfolio.waves(fee))) - - withDomain(DomainPresets.RideV6.copy(rewardsSettings = RewardsVotingSettings(None)), Seq(AddrWithBalance(senderKp.toWavesAddress))) { d => - val wavesTransfer = EthTxGenerator.generateEthTransfer(senderKp, recipient, 1.waves, Waves, fee) - assertBalanceInvariant(d.createDiff(wavesTransfer).combineF(feeDiff).explicitGet()) + val fee = TestValues.fee + withDomain(RideV6.copy(rewardsSettings = RewardsVotingSettings(None)), Seq(AddrWithBalance(senderKp.toWavesAddress))) { d => + val wavesTransfer = EthTxGenerator.generateEthTransfer(senderKp, recipient, 1.waves, Waves, fee) val transferPayload = wavesTransfer.payload.asInstanceOf[Transfer] d.appendAndAssertSucceed(wavesTransfer) + val rewardAndFee = 6.waves - wavesTransfer.fee * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, rewardAndFee) d.blockchain.balance(recipient) shouldBe transferPayload.amount d.blockchain.balance(senderKp.toWavesAddress) shouldBe ENOUGH_AMT - transferPayload.amount - fee } - withDomain(DomainPresets.RideV6, Seq(AddrWithBalance(senderKp.toWavesAddress), AddrWithBalance(issuer.toAddress))) { d => + withDomain(RideV6, Seq(AddrWithBalance(senderKp.toWavesAddress), AddrWithBalance(issuer.toAddress))) { d => val issue = TxHelpers.issue(issuer) val nativeTransfer = TxHelpers.transfer(issuer, senderKp.toWavesAddress, issue.quantity.value, issue.asset) val assetTransfer = EthTxGenerator.generateEthTransfer(senderKp, recipient, issue.quantity.value, issue.asset, fee) d.appendBlock(issue, nativeTransfer) - assertBalanceInvariant(d.createDiff(assetTransfer).combineF(feeDiff).explicitGet()) - d.appendAndAssertSucceed(assetTransfer) + val rewardAndFee = 6.waves + (issue.fee.value + nativeTransfer.fee.value - assetTransfer.fee) * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, rewardAndFee) d.blockchain.balance(recipient) shouldBe 0L d.blockchain.balance(recipient, issue.asset) shouldBe issue.quantity.value d.blockchain.balance(senderKp.toWavesAddress) shouldBe ENOUGH_AMT - assetTransfer.fee @@ -306,23 +304,23 @@ class EthereumTransactionDiffTest extends FlatSpec with WithDomain with DiffMatc Seq(Payment(321, issue.asset)) ) - val diff = d.transactionDiffer(invoke).resultE.explicitGet() - diff should containAppliedTx(invoke.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = d.transactionDiffer(invoke).resultE.explicitGet() + snapshot should containAppliedTx(invoke.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson("""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } } @@ -395,23 +393,23 @@ class EthereumTransactionDiffTest extends FlatSpec with WithDomain with DiffMatc Seq(), Seq(Payment(321, issue.asset)) ) - val diff = d.transactionDiffer(invoke).resultE.explicitGet() - diff should containAppliedTx(invoke.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = d.transactionDiffer(invoke).resultE.explicitGet() + snapshot should containAppliedTx(invoke.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson("""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } } @@ -438,24 +436,27 @@ class EthereumTransactionDiffTest extends FlatSpec with WithDomain with DiffMatc d.appendBlock(setScript) - val invoke = EthTxGenerator.generateEthInvoke(invoker, dApp.toAddress, "deposit", Seq(), Seq()) - val diff = d.transactionDiffer(invoke).resultE.explicitGet() - diff should containAppliedTx(invoke.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val invoke = EthTxGenerator.generateEthInvoke(invoker, dApp.toAddress, "deposit", Seq(), Seq()) + val snapshot = d.transactionDiffer(invoke).resultE.explicitGet() + snapshot should containAppliedTx(invoke.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson( + """ { + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + | } + """.stripMargin + ) } } @@ -526,23 +527,23 @@ class EthereumTransactionDiffTest extends FlatSpec with WithDomain with DiffMatc Seq(), Seq(Payment(321, issue.asset)) ) - val diff = d.transactionDiffer(invoke).resultE.explicitGet() - diff should containAppliedTx(invoke.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = d.transactionDiffer(invoke).resultE.explicitGet() + snapshot should containAppliedTx(invoke.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson("""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } } @@ -580,28 +581,28 @@ class EthereumTransactionDiffTest extends FlatSpec with WithDomain with DiffMatc Seq(), Nil ) - val diff = d.transactionDiffer(invoke).resultE.explicitGet() - diff should containAppliedTx(invoke.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson(s"""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | }, - | { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : "${issue.asset}", - | "amount" : 123 - | }], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = d.transactionDiffer(invoke).resultE.explicitGet() + snapshot should containAppliedTx(invoke.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson(s"""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | }, + | { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : "${issue.asset}", + | "amount" : 123 + | }], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala index 94bc8008ade..b8ce4f3a999 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala @@ -27,6 +27,7 @@ import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.transaction.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} +import com.wavesplatform.transaction.TxHelpers.defaultAddress import com.wavesplatform.transaction.TxValidationError.{AccountBalanceError, GenericError} import com.wavesplatform.transaction.assets.IssueTransaction import com.wavesplatform.transaction.assets.exchange.* @@ -40,6 +41,7 @@ import com.wavesplatform.{TestValues, TestWallet, crypto} import org.scalatest.{EitherValues, Inside} import org.web3j.crypto.Bip32ECKeyPair +import scala.concurrent.duration.* import scala.util.{Random, Try} class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain with EitherValues with TestWallet with EthHelpers { @@ -328,23 +330,27 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w } preconditionsAndExchange.foreach { case (genesis, issue1, issue2, exchange) => - assertDiffAndState( - Seq(TestBlock.create(genesis :+ issue1 :+ issue2)), - TestBlock.create(Seq(exchange), Block.ProtoBlockVersion), - fsWithOrderFeature - ) { case (blockDiff, _) => - val totalPortfolioDiff: Portfolio = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets.values.toSet should (be(Set()) or be(Set(0))) - - blockDiff.portfolios(exchange.sender.toAddress).balance shouldBe exchange.buyMatcherFee + exchange.sellMatcherFee - exchange.fee.value + withDomain(ScriptsAndSponsorship) { d => + d.appendBlock(genesis*) + d.appendBlock(issue1, issue2) + d.appendBlock(exchange) + d.liquidSnapshot.balances.toSeq + .map { + case ((`defaultAddress`, Waves), amount) => + val carryFee = (issue1.fee.value + issue2.fee.value - exchange.fee.value) * 3 / 5 + Waves -> (amount - d.rocksDBWriter.balance(defaultAddress, Waves) - carryFee) + case ((address, asset), amount) => + asset -> (amount - d.rocksDBWriter.balance(address, asset)) + } + .groupMap(_._1)(_._2) + .foreach { case (_, balances) => balances.sum shouldBe 0 } + d.liquidSnapshot.balances((exchange.sender.toAddress, Waves)) shouldBe + d.rocksDBWriter.balance(exchange.sender.toAddress) + exchange.buyMatcherFee + exchange.sellMatcherFee - exchange.fee.value } } } property("Preserves assets invariant (matcher's fee in one of the assets of the pair or in Waves), stores match info, rewards matcher") { - val preconditionsAndExchange: Seq[(Seq[GenesisTransaction], IssueTransaction, IssueTransaction, ExchangeTransaction)] = { val buyer = TxHelpers.signer(1) val seller = TxHelpers.signer(2) @@ -401,30 +407,35 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w } preconditionsAndExchange.foreach { case (genesis, issue1, issue2, exchange) => - assertDiffAndState( - Seq(TestBlock.create(genesis :+ issue1 :+ issue2)), - TestBlock.create(Seq(exchange), Block.ProtoBlockVersion), - fsWithOrderFeature - ) { case (blockDiff, _) => - val totalPortfolioDiff: Portfolio = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets.values.toSet shouldBe Set(0L) - - val matcherPortfolio = - blockDiff.portfolios.view - .filterKeys(_.toString == exchange.sender.toAddress.toString) - .values - .fold(Portfolio())(_.combine(_).explicitGet()) - - val restoredMatcherPortfolio = + withDomain(ScriptsAndSponsorship) { d => + d.appendBlock(genesis*) + d.appendBlock(issue1, issue2) + d.appendBlock(exchange) + d.liquidSnapshot.balances.toSeq + .map { + case ((`defaultAddress`, Waves), amount) => + val carryFee = (issue1.fee.value + issue2.fee.value - exchange.fee.value) * 3 / 5 + Waves -> (amount - d.rocksDBWriter.balance(defaultAddress, Waves) - carryFee) + case ((address, asset), amount) => + asset -> (amount - d.rocksDBWriter.balance(address, asset)) + } + .groupMap(_._1)(_._2) + .foreach { case (_, balances) => balances.sum shouldBe 0 } + + val sender = exchange.sender.toAddress + val expectedMatcherPortfolio = Seq( ExchangeTransactionDiff.getOrderFeePortfolio(exchange.buyOrder, exchange.buyMatcherFee), ExchangeTransactionDiff.getOrderFeePortfolio(exchange.sellOrder, exchange.sellMatcherFee), wavesPortfolio(-exchange.fee.value) ).fold(Portfolio())(_.combine(_).explicitGet()) - matcherPortfolio shouldBe restoredMatcherPortfolio + d.liquidSnapshot.balances.collect { + case ((`sender`, Waves), balance) => + balance - d.rocksDBWriter.balance(sender) shouldBe expectedMatcherPortfolio.balance + case ((`sender`, asset: IssuedAsset), balance) => + balance - d.rocksDBWriter.balance(sender, asset) shouldBe expectedMatcherPortfolio.assets(asset) + } } } } @@ -475,7 +486,6 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w } property("Preserves assets invariant (matcher's fee in separately issued asset), stores match info, rewards matcher (Orders V3 are used)") { - val preconditionsAndExchange = { val buyer = TxHelpers.signer(1) val seller = TxHelpers.signer(2) @@ -509,35 +519,41 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w matcher, version = TxVersion.V2 ) - (genesis, issue1, issue2, issue3, issue4, exchange) } } preconditionsAndExchange.foreach { case (genesis, issue1, issue2, issue3, issue4, exchange) => - assertDiffAndState( - Seq(TestBlock.create(genesis :+ issue1 :+ issue2 :+ issue3 :+ issue4)), - TestBlock.create(Seq(exchange), Block.ProtoBlockVersion), - fsWithOrderFeature - ) { case (blockDiff, _) => - val totalPortfolioDiff: Portfolio = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets.values.toSet shouldBe Set(0L) - - val matcherPortfolio = - blockDiff.portfolios.view - .filterKeys(_.toString == exchange.sender.toAddress.toString) - .values - .fold(Portfolio())(_.combine(_).explicitGet()) - - val restoredMatcherPortfolio = + withDomain(ScriptsAndSponsorship) { d => + d.appendBlock(genesis*) + d.appendBlock(issue1, issue2, issue3, issue4) + d.appendAndAssertSucceed(exchange) + + val carryFee = (issue1.fee.value + issue2.fee.value + issue3.fee.value + issue4.fee.value - exchange.fee.value) * 3 / 5 + d.liquidSnapshot.balances.toSeq + .map { + case ((`defaultAddress`, Waves), amount) => + Waves -> (amount - d.rocksDBWriter.balance(defaultAddress, Waves) - carryFee) + case ((address, asset), amount) => + asset -> (amount - d.rocksDBWriter.balance(address, asset)) + } + .groupMap(_._1)(_._2) + .foreach { case (_, balanceDiff) => balanceDiff.sum shouldBe 0 } + + val sender = exchange.sender.toAddress + val expectedMatcherPortfolio = Seq( ExchangeTransactionDiff.getOrderFeePortfolio(exchange.buyOrder, exchange.buyMatcherFee), ExchangeTransactionDiff.getOrderFeePortfolio(exchange.sellOrder, exchange.sellMatcherFee), wavesPortfolio(-exchange.fee.value) ).fold(Portfolio())(_.combine(_).explicitGet()) - matcherPortfolio shouldBe restoredMatcherPortfolio + + d.liquidSnapshot.balances.collect { + case ((`sender`, Waves), balance) => + balance - d.rocksDBWriter.balance(sender) shouldBe expectedMatcherPortfolio.balance + case ((`sender`, asset: IssuedAsset), balance) => + balance - d.rocksDBWriter.balance(sender, asset) shouldBe expectedMatcherPortfolio.assets(asset) + } } } } @@ -636,26 +652,25 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w } property("Total matcher's fee (sum of matcher's fees in exchange transactions) is less than or equal to order's matcher fee") { - val preconditions = oneBuyFewSellsPreconditions( - totalBuyMatcherFeeBoundaries = - (bigBuyOrderMatcherFee: Long) => - (bigBuyOrderMatcherFee - 1000L, bigBuyOrderMatcherFee), // sum of buyMatcherFee in ex trs <= specified in bigBuyOrder + totalBuyMatcherFeeBoundaries = identity, sellersTotalAmount = identity ) val (genesises, issueTx1, issueTx2, massTransfer, exchanges, bigBuyOrder) = preconditions - assertDiffAndState( - Seq(TestBlock.create(genesises), TestBlock.create(Seq(issueTx1, issueTx2, massTransfer), Block.ProtoBlockVersion)), - TestBlock.create(exchanges, Block.ProtoBlockVersion), - fsOrderMassTransfer - ) { case (blockDiff, _) => - val totalPortfolioDiff: Portfolio = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets.values.toSet shouldBe Set(0L) + withDomain(RideV3) { d => + d.appendBlock(genesises*) + d.appendBlock(issueTx1, issueTx2, massTransfer) + d.appendBlock(exchanges*) + val carryFee = (issueTx1.fee.value + issueTx2.fee.value + massTransfer.fee.value - exchanges.map(_.fee.value).sum) * 3 / 5 + d.liquidSnapshot.balances.toSeq + .map { + case ((`defaultAddress`, Waves), amount) => Waves -> (amount - d.rocksDBWriter.balance(defaultAddress, Waves) - carryFee) + case ((address, asset), amount) => asset -> (amount - d.rocksDBWriter.balance(address, asset)) + } + .groupMap(_._1)(_._2) + .foreach { case (_, balanceDiff) => balanceDiff.sum shouldBe 0 } val combinedPortfolio = exchanges.map(ex => getOrderFeePortfolio(bigBuyOrder, ex.buyMatcherFee)).fold(Portfolio())(_.combine(_).explicitGet()) @@ -673,8 +688,7 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w val preconditions = oneBuyFewSellsPreconditions( totalBuyMatcherFeeBoundaries = - (bigBuyOrderMatcherFee: Long) => - (bigBuyOrderMatcherFee + 1, bigBuyOrderMatcherFee + 100000L), // sum of buyMatcherFee in ex trs > specified in bigBuyOrder + (bigBuyOrderMatcherFee: Long) => bigBuyOrderMatcherFee + 100000L, // sum of buyMatcherFee in ex trs > specified in bigBuyOrder sellersTotalAmount = identity ) @@ -692,9 +706,8 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w val preconditions = oneBuyFewSellsPreconditions( - totalBuyMatcherFeeBoundaries = - (bigBuyOrderMatcherFee: Long) => (bigBuyOrderMatcherFee - 10000L, bigBuyOrderMatcherFee), // correct total buyMatcherFee in ex trs - sellersTotalAmount = (bigBuyOrderAmount: Long) => bigBuyOrderAmount + 10000L // sell orders overfill buy order + totalBuyMatcherFeeBoundaries = (bigBuyOrderMatcherFee: Long) => bigBuyOrderMatcherFee, // correct total buyMatcherFee in ex trs + sellersTotalAmount = (bigBuyOrderAmount: Long) => bigBuyOrderAmount + 10000L // sell orders overfill buy order ) val (genesises, issueTx1, issueTx2, massTransfer, exchanges, _) = preconditions @@ -762,17 +775,23 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w } preconditions.foreach { case (genesis, issue, exchange) => - assertDiffAndState( - Seq(TestBlock.create(genesis :+ issue)), - TestBlock.create(Seq(exchange), Block.ProtoBlockVersion), - fsWithOrderFeature - ) { case (blockDiff, _) => - val totalPortfolioDiff: Portfolio = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets.values.toSet shouldBe Set(0L) - - blockDiff.portfolios(exchange.sender.toAddress).balance shouldBe exchange.buyMatcherFee + exchange.sellMatcherFee - exchange.fee.value + withDomain(ScriptsAndSponsorship) { d => + d.appendBlock(genesis*) + d.appendBlock(issue) + d.appendBlock(exchange) + d.liquidSnapshot.balances.toSeq + .map { + case ((`defaultAddress`, Waves), amount) => + val carryFee = (issue.fee.value - exchange.fee.value) * 3 / 5 + Waves -> (amount - d.rocksDBWriter.balance(defaultAddress, Waves) - carryFee) + case ((address, asset), amount) => + asset -> (amount - d.rocksDBWriter.balance(address, asset)) + } + .groupMap(_._1)(_._2) + .foreach { case (_, balanceDiff) => balanceDiff.sum shouldBe 0 } + + d.liquidSnapshot.balances((exchange.sender.toAddress, Waves)) shouldBe + d.rocksDBWriter.balance(exchange.sender.toAddress, Waves) + exchange.buyMatcherFee + exchange.sellMatcherFee - exchange.fee.value } assertDiffEi( @@ -819,8 +838,8 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w TxHelpers.order(OrderType.BUY, issue.asset, Waves, amount = 1000000L, fee = MatcherFee, sender = buyer, matcher = matcher, version = Order.V1) val sell = TxHelpers.order(OrderType.SELL, issue.asset, Waves, fee = MatcherFee, sender = seller, matcher = matcher, version = Order.V1) val tx = createExTx(buy, sell, buy.price.value, matcher) - assertDiffAndState(Seq(TestBlock.create(genesis :+ issue)), TestBlock.create(Seq(tx)), fs) { case (blockDiff, state) => - blockDiff.portfolios(tx.sender.toAddress).balance shouldBe tx.buyMatcherFee + tx.sellMatcherFee - tx.fee.value + assertDiffAndState(Seq(TestBlock.create(genesis :+ issue)), TestBlock.create(Seq(tx)), fs) { case (snapshot, state) => + snapshot.balances((tx.sender.toAddress, Waves)) shouldBe tx.buyMatcherFee + tx.sellMatcherFee - tx.fee.value state.balance(tx.sender.toAddress) shouldBe 1L } } @@ -861,14 +880,14 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w version = Order.V1 ) val tx = createExTx(buy, sell, buy.price.value, matcher) - assertDiffEi(Seq(TestBlock.create(genesis :+ issue)), TestBlock.create(Seq(tx)), fsWithOrderFeature) { totalDiffEi => - inside(totalDiffEi) { case Left(TransactionValidationError(AccountBalanceError(errs), _)) => + assertDiffEi(Seq(TestBlock.create(genesis :+ issue)), TestBlock.create(Seq(tx)), fsWithOrderFeature) { snapshotEi => + inside(snapshotEi) { case Left(TransactionValidationError(AccountBalanceError(errs), _)) => errs should contain key seller.toAddress } } } - property("Diff for ExchangeTransaction works as expected and doesn't use rounding inside") { + property("StateSnapshot for ExchangeTransaction works as expected and doesn't use rounding inside") { val MatcherFee = 300000L val preconditions: (KeyPair, KeyPair, KeyPair, Seq[GenesisTransaction], IssueTransaction) = { @@ -917,14 +936,13 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w fee = buy.matcherFee.value, version = TxVersion.V1 ) - - assertDiffEi(Seq(TestBlock.create(genesis :+ issue)), TestBlock.create(Seq(tx))) { totalDiffEi => - inside(totalDiffEi) { case Right(diff) => - import diff.portfolios - portfolios(buyer.toAddress).balance shouldBe (-41L + 425532L) - portfolios(seller.toAddress).balance shouldBe (-300000L - 425532L) - portfolios(matcher.toAddress).balance shouldBe (+41L + 300000L - tx.fee.value) - } + withDomain(ScriptsAndSponsorship) { d => + d.appendBlock(genesis*) + d.appendBlock(issue) + d.appendBlock(tx) + d.liquidSnapshot.balances((buyer.toAddress, Waves)) shouldBe d.rocksDBWriter.balance(buyer.toAddress) - 41 + 425532 + d.liquidSnapshot.balances((seller.toAddress, Waves)) shouldBe d.rocksDBWriter.balance(seller.toAddress) - 300000 - 425532 + d.liquidSnapshot.balances((matcher.toAddress, Waves)) shouldBe d.rocksDBWriter.balance(seller.toAddress) + 41 + 300000 - tx.fee.value } } @@ -983,8 +1001,8 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w TestBlock.create(transfers), TestBlock.create(issueAndScripts) ) - assertDiffEi(preconBlocks, TestBlock.create(Seq(exchangeTx)), fsV2) { diff => - diff.isRight shouldBe true + assertDiffEi(preconBlocks, TestBlock.create(Seq(exchangeTx)), fsV2) { snapshot => + snapshot.isRight shouldBe true } } } @@ -1296,9 +1314,7 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w ) val test = TestBlock.create(Seq(exchangeTx)) if (o1.version == 2 && o2.version == 2) { - assertDiffEi(pretest, test, fs) { diff => - diff.explicitGet() - } + assertDiffEi(pretest, test, fs)(_.explicitGet()) } else { assertLeft(pretest, test, fs)("Can't process order with signature from scripted account") } @@ -1505,7 +1521,7 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w ) forAll(scenarios) { case (txWithV3, txWithV4AsV3, txWithV4, txWithV3V4AsV3, txWithV3V4, txWithV4AsV3V3, txWithV4V3) => - val portfolios = collection.mutable.ListBuffer[Map[Address, Portfolio]]() + val balances = collection.mutable.ListBuffer[Map[(Address, Asset), Long]]() Seq(txWithV3, txWithV4AsV3, txWithV4, txWithV3V4AsV3, txWithV3V4, txWithV4AsV3V3, txWithV4V3) .foreach { tx => @@ -1513,13 +1529,13 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w preconditions, TestBlock.create(Seq(tx), Block.ProtoBlockVersion), DomainPresets.RideV6.blockchainSettings.functionalitySettings - ) { case (blockDiff, _) => - portfolios += blockDiff.portfolios + ) { case (snapshot, _) => + balances += snapshot.balances } } // all portfolios built on the state and on the composite blockchain are equal - portfolios.distinct.size shouldBe 1 + balances.distinct.size shouldBe 1 } } @@ -1592,14 +1608,14 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w } scenario.foreach { case (preconditions, fixed, reversed) => - val portfolios = collection.mutable.ListBuffer[Map[Address, Portfolio]]() + val portfolios = collection.mutable.ListBuffer[Map[(Address, Asset), Long]]() - assertDiffAndState(preconditions, TestBlock.create(Seq(fixed)), fsWithBlockV5) { case (diff, _) => - portfolios += diff.portfolios + assertDiffAndState(preconditions, TestBlock.create(Seq(fixed)), fsWithBlockV5) { case (snapshot, _) => + portfolios += snapshot.balances } - assertDiffAndState(preconditions, TestBlock.create(Seq(reversed)), fsWithBlockV5) { case (diff, _) => - portfolios += diff.portfolios + assertDiffAndState(preconditions, TestBlock.create(Seq(reversed)), fsWithBlockV5) { case (snapshot, _) => + portfolios += snapshot.balances } portfolios.tail.forall(_ == portfolios.head) shouldBe true @@ -1669,33 +1685,35 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w assertDiffEi(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(exchange), Block.ProtoBlockVersion), fsWithOrderFeature) { ei => ei.left.value } - assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(exchange), Block.ProtoBlockVersion), fsWithBlockV5) { - case (diff, state) => - diff.scriptsComplexity shouldBe 1 // throw() - diff.portfolios(exchange.sender.toAddress).balance shouldBe -exchange.fee.value - diff.portfolios.get(exchange.buyOrder.sender.toAddress) shouldBe None - diff.portfolios.get(exchange.sellOrder.sender.toAddress) shouldBe None - - diff.scriptsComplexity shouldBe DiffsCommon - .countVerifierComplexity(Some(throwingScript), state, isAsset = true) - .explicitGet() - .get - ._2 - - buyerBalance.foreach { case (asset, balance) => - state.balance(exchange.buyOrder.sender.toAddress, asset) shouldBe balance - } - sellerBalance.foreach { case (asset, balance) => - state.balance(exchange.sellOrder.sender.toAddress, asset) shouldBe balance - } + withDomain(RideV4) { d => + d.appendBlock(genesisTxs*) + d.appendBlock(exchange) + d.liquidSnapshot.scriptsComplexity shouldBe 1 // throw() + d.liquidSnapshot + .balances((exchange.sender.toAddress, Waves)) shouldBe d.rocksDBWriter.balance(exchange.sender.toAddress, Waves) - exchange.fee.value + d.liquidSnapshot.balances.get((exchange.buyOrder.sender.toAddress, Waves)) shouldBe None + d.liquidSnapshot.balances.get((exchange.sellOrder.sender.toAddress, Waves)) shouldBe None + + d.liquidSnapshot.scriptsComplexity shouldBe DiffsCommon + .countVerifierComplexity(Some(throwingScript), d.blockchain, isAsset = true) + .explicitGet() + .get + ._2 - state.balance(exchange.sender.toAddress, Waves) shouldBe matcherBalance(Waves) - exchange.fee.value - matcherBalance.collect { case b @ (IssuedAsset(_), _) => b }.foreach { case (asset, balance) => - diff.portfolios(exchange.sender.toAddress).balanceOf(asset) shouldBe 0L - state.balance(exchange.sender.toAddress, asset) shouldBe balance - } + buyerBalance.foreach { case (asset, balance) => + d.balance(exchange.buyOrder.sender.toAddress, asset) shouldBe balance + } + sellerBalance.foreach { case (asset, balance) => + d.balance(exchange.sellOrder.sender.toAddress, asset) shouldBe balance + } + + d.balance(exchange.sender.toAddress, Waves) shouldBe matcherBalance(Waves) - exchange.fee.value + matcherBalance.collect { case b @ (IssuedAsset(_), _) => b }.foreach { case (asset, balance) => + d.liquidSnapshot.balances.get((exchange.sender.toAddress, asset)) shouldBe None + d.balance(exchange.sender.toAddress, asset) shouldBe balance + } - state.transactionInfo(exchange.id()).map(r => r._2 -> (r._1.status == Status.Succeeded)) shouldBe Some((exchange, false)) + d.blockchain.transactionInfo(exchange.id()).map(r => r._2 -> (r._1.status == Status.Succeeded)) shouldBe Some((exchange, false)) } } } @@ -1726,7 +1744,7 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w withDomain(RideV4) { d => d.appendBlock(Seq(amountAssetIssue, priceAssetIssue, order1FeeAssetIssue, order2FeeAssetIssue).distinct*) d.appendAndAssertFailed(exchange) - d.liquidDiff.scriptsComplexity shouldBe complexity + d.liquidSnapshot.scriptsComplexity shouldBe complexity } } @@ -1798,10 +1816,10 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w ) { d => d.appendBlock(Seq(tradeableAssetIssue, feeAssetIssue).distinct*) val newBlock = d.createBlock(2.toByte, Seq(exchange)) - val diff = BlockDiffer + val snapshot = BlockDiffer .fromBlock(d.blockchainUpdater, Some(d.lastBlock), newBlock, None, MiningConstraint.Unlimited, newBlock.header.generationSignature) .explicitGet() - diff.snapshot.scriptsComplexity shouldBe complexity + snapshot.snapshot.scriptsComplexity shouldBe complexity val feeUnits = FeeValidation.getMinFee(d.blockchainUpdater, exchange).explicitGet().minFeeInWaves / FeeValidation.FeeUnit if (complexity > 0) feeUnits shouldBe 7 @@ -1907,10 +1925,10 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w defaultSigner.publicKey, AssetPair(issue.asset, Waves), OrderType.BUY, - TxExchangeAmount.unsafeFrom(1), - TxOrderPrice.unsafeFrom(1), + TxExchangeAmount(1), + TxOrderPrice(1), System.currentTimeMillis(), - System.currentTimeMillis() + 10000, + System.currentTimeMillis() + 10.hours.toMillis, TxMatcherFee.unsafeFrom(0.003.waves) ) val signedBuyOrder = buyOrder.copy( @@ -1973,10 +1991,10 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w defaultSigner.publicKey, AssetPair(issue.asset, Waves), OrderType.BUY, - TxExchangeAmount.unsafeFrom(1), - TxOrderPrice.unsafeFrom(1), + TxExchangeAmount(1), + TxOrderPrice(1), System.currentTimeMillis(), - System.currentTimeMillis() + 10000, + System.currentTimeMillis() + 10.hours.toMillis, TxMatcherFee.unsafeFrom(0.003.waves) ) val signature = EthOrders.signOrder(buyOrder, signer) @@ -2184,7 +2202,7 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w totalMatcherFee: Long ): Seq[Order] = { val randomAmountsAndFees = - getSeqWithPredefinedSum(totalAmount, sellers.length) zip getSeqWithPredefinedSum(totalMatcherFee, sellers.length) + getSeqWithPredefinedSum(totalAmount, sellers.length) zip getSeqWithPredefinedSum(totalMatcherFee / 10, sellers.length).map(_ * 10) val sellers2AmountsAndFees = sellers zip randomAmountsAndFees @@ -2211,7 +2229,7 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w * function for manipulating of total sell orders amount */ def oneBuyFewSellsPreconditions( - totalBuyMatcherFeeBoundaries: Long => (Long, Long), + totalBuyMatcherFeeBoundaries: Long => Long, sellersTotalAmount: Long => Long ): (Seq[GenesisTransaction], IssueTransaction, IssueTransaction, MassTransferTransaction, Seq[ExchangeTransaction], Order) = { val matcher = TxHelpers.signer(1) @@ -2220,12 +2238,12 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w val buyer = TxHelpers.signer(sellOrdersCount + 2) val bigBuyOrderAmount = 3 * 100000L * 100000000L val price = 3 * 100000L - val bigBuyOrderMatcherFee = 100000L + val bigBuyOrderMatcherFee = 1000000L - val issue1 = TxHelpers.issue(buyer, Long.MaxValue - 1000L, name = "asset1") - val issue2 = TxHelpers.issue(buyer, Long.MaxValue - 1000L, name = "asset2") + val issue1 = TxHelpers.issue(buyer, Long.MaxValue - 1_000_000, name = "asset1") + val issue2 = TxHelpers.issue(buyer, Long.MaxValue - 1_000_000, name = "asset2") - val totalBuyMatcherFeeForExchangeTransactions = totalBuyMatcherFeeBoundaries(bigBuyOrderMatcherFee)._2 + val totalBuyMatcherFeeForExchangeTransactions = totalBuyMatcherFeeBoundaries(bigBuyOrderMatcherFee) val bigBuyOrder = TxHelpers.order( orderType = OrderType.BUY, @@ -2255,11 +2273,11 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w from = buyer, to = sellers.map(seller => ParsedTransfer(seller.toAddress, TxNonNegativeAmount.unsafeFrom(issue2.quantity.value / sellOrdersCount))), asset = issue2.asset, - fee = 1000L, + fee = 1_000_000, version = TxVersion.V1 ) - val buyMatcherFees = getSeqWithPredefinedSum(totalBuyMatcherFeeForExchangeTransactions, sellOrdersCount) + val buyMatcherFees = getSeqWithPredefinedSum(totalBuyMatcherFeeForExchangeTransactions / 10, sellOrdersCount).map(_ * 10) val exchanges = (sellOrders zip buyMatcherFees).map { case (sellOrder, buyMatcherFee) => TxHelpers.exchange( diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/GenesisTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/GenesisTransactionDiffTest.scala index a8b01f33b1d..081c29be129 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/GenesisTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/GenesisTransactionDiffTest.scala @@ -1,13 +1,13 @@ package com.wavesplatform.state.diffs -import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.db.WithDomain import com.wavesplatform.lagonaki.mocks.TestBlock -import com.wavesplatform.state.* import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.RideV6 +import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.TxHelpers import com.wavesplatform.transaction.TxHelpers.{defaultAddress, genesis} +import org.scalatest.exceptions.TestFailedException class GenesisTransactionDiffTest extends PropSpec with WithDomain { property("fails if height != 1") { @@ -17,17 +17,16 @@ class GenesisTransactionDiffTest extends PropSpec with WithDomain { } } - property("Diff establishes Waves invariant") { + property("StateSnapshot establishes Waves invariant") { val genesis = (1 to 10).map(idx => TxHelpers.genesis(TxHelpers.address(idx), 10000)) - assertDiffAndState(Seq.empty, TestBlock.create(genesis)) { (blockDiff, _) => - val totalPortfolio = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolio.balance shouldBe genesis.map(_.amount.value).sum - totalPortfolio.effectiveBalance(false).explicitGet() shouldBe genesis.map(_.amount.value).sum - totalPortfolio.assets shouldBe Map.empty - + blockDiff.balances.collect { + case ((_, Waves), amount) => amount + case ((_, asset), _) => throw new TestFailedException(s"unexpected $asset", 0) + }.sum shouldBe genesis.map(_.amount.value).sum + blockDiff.leaseBalances shouldBe empty genesis.foreach { gtx => - blockDiff.portfolios(gtx.recipient).balance shouldBe gtx.amount.value + blockDiff.balances((gtx.recipient, Waves)) shouldBe gtx.amount.value } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/LeaseTransactionsDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/LeaseTransactionsDiffTest.scala index d789f1853f5..be960af7373 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/LeaseTransactionsDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/LeaseTransactionsDiffTest.scala @@ -12,10 +12,13 @@ import com.wavesplatform.settings.TestFunctionalitySettings import com.wavesplatform.state.* import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.* +import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} import com.wavesplatform.transaction.transfer.* import com.wavesplatform.transaction.{GenesisTransaction, TxHelpers, TxVersion} +import scala.collection.immutable.VectorMap + class LeaseTransactionsDiffTest extends PropSpec with WithDomain { private val allowMultipleLeaseCancelTransactionUntilTimestamp = Long.MaxValue / 2 @@ -25,33 +28,33 @@ class LeaseTransactionsDiffTest extends PropSpec with WithDomain { def total(l: LeaseBalance): Long = l.in - l.out property("can lease/cancel lease preserving waves invariant") { - - val sunnyDayLeaseLeaseCancel: Seq[(GenesisTransaction, LeaseTransaction, LeaseCancelTransaction)] = { - val master = TxHelpers.signer(1) - val recipient = TxHelpers.signer(2) - - val genesis = TxHelpers.genesis(master.toAddress) - for { - lease <- Seq(TxHelpers.lease(master, recipient.toAddress), TxHelpers.lease(master, recipient.toAddress, version = TxVersion.V1)) - leaseCancel <- Seq(TxHelpers.leaseCancel(lease.id(), master), TxHelpers.leaseCancel(lease.id(), master, version = TxVersion.V1)) - } yield (genesis, lease, leaseCancel) - } - - sunnyDayLeaseLeaseCancel.foreach { case (genesis, lease, leaseCancel) => - assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(lease))) { case (totalDiff, _) => - val totalPortfolioDiff = totalDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - total(totalPortfolioDiff.lease) shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets.values.foreach(_ shouldBe 0) + val sender = TxHelpers.signer(1) + val recipient = TxHelpers.signer(2) + val miner = TestBlock.defaultSigner.toAddress + val genesis = TxHelpers.genesis(sender.toAddress) + for { + lease <- Seq(TxHelpers.lease(sender, recipient.toAddress), TxHelpers.lease(sender, recipient.toAddress, version = TxVersion.V1)) + leaseCancel <- Seq(TxHelpers.leaseCancel(lease.id(), sender), TxHelpers.leaseCancel(lease.id(), sender, version = TxVersion.V1)) + } { + assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(lease))) { case (snapshot, _) => + snapshot.balances shouldBe VectorMap( + (sender.toAddress, Waves) -> (genesis.amount.value - lease.fee.value), + (miner, Waves) -> lease.fee.value + ) + snapshot.leaseBalances shouldBe Map( + sender.toAddress -> LeaseBalance(0, lease.amount.value), + recipient.toAddress -> LeaseBalance(lease.amount.value, 0) + ) } - - assertDiffAndState(Seq(TestBlock.create(Seq(genesis, lease))), TestBlock.create(Seq(leaseCancel))) { case (totalDiff, _) => - val totalPortfolioDiff = totalDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - total(totalPortfolioDiff.lease) shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 - totalPortfolioDiff.assets.values.foreach(_ shouldBe 0) + assertDiffAndState(Seq(TestBlock.create(Seq(genesis, lease))), TestBlock.create(Seq(leaseCancel))) { case (snapshot, _) => + snapshot.balances shouldBe VectorMap( + (miner, Waves) -> (lease.fee.value + leaseCancel.fee.value), + (sender.toAddress, Waves) -> (genesis.amount.value - lease.fee.value - leaseCancel.fee.value) + ) + snapshot.leaseBalances shouldBe Map( + sender.toAddress -> LeaseBalance.empty, + recipient.toAddress -> LeaseBalance.empty + ) } } } @@ -96,8 +99,8 @@ class LeaseTransactionsDiffTest extends PropSpec with WithDomain { property("cannot cancel lease twice after allowMultipleLeaseCancelTransactionUntilTimestamp") { disallowCancelTwice.foreach { case (preconditions, block) => - assertDiffEi(preconditions, block, settings) { totalDiffEi => - totalDiffEi should produce("Cannot cancel already cancelled lease") + assertDiffEi(preconditions, block, settings) { snapshotEi => + snapshotEi should produce("Cannot cancel already cancelled lease") } } } @@ -112,8 +115,8 @@ class LeaseTransactionsDiffTest extends PropSpec with WithDomain { property("can cancel lease twice before allowMultipleLeaseCancelTransactionUntilTimestamp") { allowCancelTwice.foreach { case (preconditions, block) => - assertDiffEi(preconditions, block, settings) { totalDiffEi => - totalDiffEi.explicitGet() + assertDiffEi(preconditions, block, settings) { snapshotEi => + snapshotEi.explicitGet() } } } @@ -133,8 +136,8 @@ class LeaseTransactionsDiffTest extends PropSpec with WithDomain { } setup.foreach { case (genesis, lease, leaseForward, ts) => - assertDiffEi(Seq(TestBlock.create(ts, Seq(genesis, lease))), TestBlock.create(ts, Seq(leaseForward)), settings) { totalDiffEi => - totalDiffEi should produce("Cannot lease more than own") + assertDiffEi(Seq(TestBlock.create(ts, Seq(genesis, lease))), TestBlock.create(ts, Seq(leaseForward)), settings) { snapshotEi => + snapshotEi should produce("Cannot lease more than own") } } } @@ -171,18 +174,23 @@ class LeaseTransactionsDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(blockTime, genesis :+ lease)), TestBlock.create(blockTime, Seq(unleaseOtherOrRecipient)), settings - ) { totalDiffEi => - totalDiffEi should produce("LeaseTransaction was leased by other sender") + ) { snapshotEi => + snapshotEi should produce("LeaseTransaction was leased by other sender") } } } property("can cancel lease of another sender and acquire leasing power before allowMultipleLeaseCancelTransactionUntilTimestamp") { cancelLeaseOfAnotherSender(unleaseByRecipient = false, repeatedCancelAllowed).foreach { case (genesis, lease, unleaseOther, blockTime) => - assertDiffAndState(Seq(TestBlock.create(genesis :+ lease)), TestBlock.create(blockTime, Seq(unleaseOther)), settings) { case (totalDiff, _) => - totalDiff.portfolios.get(lease.sender.toAddress) shouldBe None - total(totalDiff.portfolios(lease.recipient.asInstanceOf[Address]).lease) shouldBe -lease.amount.value - total(totalDiff.portfolios(unleaseOther.sender.toAddress).lease) shouldBe lease.amount.value + withDomain(ScriptsAndSponsorship.configure(_.copy(lastTimeBasedForkParameter = allowMultipleLeaseCancelTransactionUntilTimestamp))) { d => + d.appendBlock(genesis*) + d.appendBlock(lease) + d.appendBlock(TestBlock.create(blockTime, d.lastBlockId, Seq(unleaseOther)).block) + d.liquidSnapshot.balances.get((lease.sender.toAddress, Waves)) shouldBe None + val recipient = lease.recipient.asInstanceOf[Address] + val unleaser = unleaseOther.sender.toAddress + total(d.liquidSnapshot.leaseBalances(recipient)) shouldBe total(d.rocksDBWriter.leaseBalance(recipient)) - lease.amount.value + total(d.liquidSnapshot.leaseBalances(unleaser)) shouldBe total(d.rocksDBWriter.leaseBalance(unleaser)) + lease.amount.value } } } @@ -191,13 +199,13 @@ class LeaseTransactionsDiffTest extends PropSpec with WithDomain { "if recipient cancels lease, it doesn't change leasing component of mining power before allowMultipleLeaseCancelTransactionUntilTimestamp" ) { cancelLeaseOfAnotherSender(unleaseByRecipient = true, repeatedCancelAllowed).foreach { case (genesis, lease, unleaseRecipient, blockTime) => - assertDiffAndState( - Seq(TestBlock.create(blockTime, genesis :+ lease)), - TestBlock.create(blockTime, Seq(unleaseRecipient)), - settings - ) { case (totalDiff, _) => - totalDiff.portfolios.get(lease.sender.toAddress) shouldBe None - total(totalDiff.portfolios(unleaseRecipient.sender.toAddress).lease) shouldBe 0 + withDomain(ScriptsAndSponsorship.configure(_.copy(lastTimeBasedForkParameter = allowMultipleLeaseCancelTransactionUntilTimestamp))) { d => + d.appendBlock(genesis*) + d.appendBlock(lease) + d.appendBlock(TestBlock.create(blockTime, d.lastBlockId, Seq(unleaseRecipient)).block) + d.liquidSnapshot.balances.get((lease.sender.toAddress, Waves)) shouldBe None + val unleaser = unleaseRecipient.sender.toAddress + total(d.liquidSnapshot.leaseBalances(unleaser)) shouldBe total(d.rocksDBWriter.leaseBalance(unleaser)) } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala index 5ad175a2321..1a534bb85cc 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala @@ -3,16 +3,17 @@ package com.wavesplatform.state.diffs import com.wavesplatform.account.{Address, Alias, KeyPair} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.db.WithState +import com.wavesplatform.db.WithDomain import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lagonaki.mocks.TestBlock.create as block import com.wavesplatform.settings.{FunctionalitySettings, TestFunctionalitySettings} import com.wavesplatform.test.* +import com.wavesplatform.test.DomainPresets.ScriptsAndSponsorship import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer import com.wavesplatform.transaction.{Asset, GenesisTransaction, TxHelpers, TxNonNegativeAmount, TxVersion} -class MassTransferTransactionDiffTest extends PropSpec with WithState { +class MassTransferTransactionDiffTest extends PropSpec with WithDomain { val fs: FunctionalitySettings = TestFunctionalitySettings.Enabled.copy(preActivatedFeatures = Map(BlockchainFeatures.MassTransfer.id -> 0)) @@ -35,42 +36,45 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { Seq(Some(issue.id()), None).map { issueIdOpt => val maybeAsset = Asset.fromCompatId(issueIdOpt) - val transfer = TxHelpers.massTransfer(master, transfers, maybeAsset, version = TxVersion.V1) + val transfer = TxHelpers.massTransfer(master, transfers, maybeAsset, fee = 1.waves, version = TxVersion.V1) (genesis, issue, transfer) } } - setup.foreach { - case (genesis, issue, transfer) => - assertDiffAndState(Seq(block(Seq(genesis, issue))), block(Seq(transfer)), fs) { - case (totalDiff, newState) => - assertBalanceInvariant(totalDiff) - - val totalAmount = transfer.transfers.map(_.amount.value).sum - val fees = issue.fee.value + transfer.fee.value + setup.foreach { case (genesis, issue, transfer) => + withDomain(ScriptsAndSponsorship) { d => + d.appendBlock(genesis) + d.appendBlock(issue) + d.appendBlock(transfer) + + val carryFee = (issue.fee.value - transfer.fee.value) * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, carryFee) + + val totalAmount = transfer.transfers.map(_.amount.value).sum + val fees = issue.fee.value + transfer.fee.value + transfer.assetId match { + case aid @ IssuedAsset(_) => + d.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees + d.balance(transfer.sender.toAddress, aid) shouldBe ENOUGH_AMT - totalAmount + case Waves => + d.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees - totalAmount + } + for (ParsedTransfer(recipient, amount) <- transfer.transfers) { + if (transfer.sender.toAddress != recipient) { transfer.assetId match { case aid @ IssuedAsset(_) => - newState.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees - newState.balance(transfer.sender.toAddress, aid) shouldBe ENOUGH_AMT - totalAmount + d.balance(recipient.asInstanceOf[Address], aid) shouldBe amount.value case Waves => - newState.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees - totalAmount - } - for (ParsedTransfer(recipient, amount) <- transfer.transfers) { - if (transfer.sender.toAddress != recipient) { - transfer.assetId match { - case aid @ IssuedAsset(_) => - newState.balance(recipient.asInstanceOf[Address], aid) shouldBe amount.value - case Waves => - newState.balance(recipient.asInstanceOf[Address]) shouldBe amount.value - } - } + d.balance(recipient.asInstanceOf[Address]) shouldBe amount.value } + } } + } } } - import com.wavesplatform.transaction.transfer.MassTransferTransaction.{MaxTransferCount => Max} + import com.wavesplatform.transaction.transfer.MassTransferTransaction.MaxTransferCount as Max Seq(0, 1, Max) foreach testDiff // test edge cases testDiff(5) } @@ -79,7 +83,7 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { val setup = { val (genesis, master) = baseSetup val recipient = Alias.create("alias").explicitGet() - val transfer = TxHelpers.massTransfer(master, Seq(ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(100000L))), version = TxVersion.V1) + val transfer = TxHelpers.massTransfer(master, Seq(ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(100000L))), version = TxVersion.V1) (genesis, transfer) } @@ -120,11 +124,10 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { } } - setup.foreach { - case (genesis, transfer) => - assertDiffEi(Seq(block(Seq(genesis))), block(Seq(transfer)), fs) { blockDiffEi => - blockDiffEi should produce("Attempt to transfer unavailable funds") - } + setup.foreach { case (genesis, transfer) => + assertDiffEi(Seq(block(Seq(genesis))), block(Seq(transfer)), fs) { blockDiffEi => + blockDiffEi should produce("Attempt to transfer unavailable funds") + } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/PaymentTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/PaymentTransactionDiffTest.scala index 5cf85cf7ea7..135bc57fdd6 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/PaymentTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/PaymentTransactionDiffTest.scala @@ -1,10 +1,8 @@ package com.wavesplatform.state.diffs -import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.db.WithState import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.settings.{FunctionalitySettings, TestFunctionalitySettings} -import com.wavesplatform.state.* import com.wavesplatform.test.* import com.wavesplatform.transaction.{GenesisTransaction, PaymentTransaction, TxHelpers} @@ -23,12 +21,11 @@ class PaymentTransactionDiffTest extends PropSpec with WithState { val settings: FunctionalitySettings = TestFunctionalitySettings.Enabled.copy(blockVersion3AfterHeight = 2) - property("Diff doesn't break invariant before block version 3") { - preconditionsAndPayments.foreach { case ((genesis, paymentV2, _)) => - assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(paymentV2)), settings) { (blockDiff, newState) => - val totalPortfolioDiff: Portfolio = blockDiff.portfolios.values.fold(Portfolio())(_.combine(_).explicitGet()) - totalPortfolioDiff.balance shouldBe 0 - totalPortfolioDiff.effectiveBalance(false).explicitGet() shouldBe 0 + property("StateSnapshot doesn't break invariant before block version 3") { + preconditionsAndPayments.foreach { case (genesis, paymentV2, _) => + assertDiffAndState(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(paymentV2)), settings) { (blockDiff, blockchain) => + blockDiff.balances.map { case ((address, asset), amount) => blockchain.balance(address, asset) - amount }.sum shouldBe 0 + blockDiff.leaseBalances shouldBe empty } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala index 20641ba4523..bd8296a5d23 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/SetScriptTransactionDiffTest.scala @@ -435,7 +435,7 @@ class SetScriptTransactionDiffTest extends PropSpec with WithDomain { withDomain(domainSettingsWithFS(settings()), balances) { db => val tx = setScript() db.appendBlock(tx) - db.liquidDiff.errorMessage(tx.id()) shouldBe None + db.liquidSnapshot.errorMessage(tx.id()) shouldBe None } withDomain(domainSettingsWithFS(settings(checkNegative = true)), balances) { db => diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/DiffProduceError.scala b/node/src/test/scala/com/wavesplatform/state/diffs/SnapshotProduceError.scala similarity index 73% rename from node/src/test/scala/com/wavesplatform/state/diffs/DiffProduceError.scala rename to node/src/test/scala/com/wavesplatform/state/diffs/SnapshotProduceError.scala index 1412039e6e2..aabc0577707 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/DiffProduceError.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/SnapshotProduceError.scala @@ -1,13 +1,13 @@ package com.wavesplatform.state.diffs -import com.wavesplatform.state.Diff +import com.wavesplatform.state.StateSnapshot import org.scalatest.matchers.{MatchResult, Matcher} -class DiffProduceError(errorMessage: String, requireFailed: Boolean) extends Matcher[Either[_, _]] { +class SnapshotProduceError(errorMessage: String, requireFailed: Boolean) extends Matcher[Either[_, _]] { override def apply(ei: Either[_, _]): MatchResult = { ei match { - case r @ Right(diff: Diff) => - diff.scriptResults.values.find(_.error.exists(_.text.contains(errorMessage))) match { + case r @ Right(snapshot: StateSnapshot) => + snapshot.scriptResults.values.find(_.error.exists(_.text.contains(errorMessage))) match { case Some(_) => MatchResult(matches = true, "", "", Vector.empty) case None => MatchResult(matches = false, "expecting Left(...{0}...) but got {1}", "got expected error", IndexedSeq(errorMessage, r)) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/SponsorshipDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/SponsorshipDiffTest.scala index 4d281bf0267..85dc1cff51b 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/SponsorshipDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/SponsorshipDiffTest.scala @@ -36,16 +36,16 @@ class SponsorshipDiffTest extends PropSpec with WithState { val cancel = TxHelpers.sponsor(issue.asset, None, master, fee = sponsorTxFee) val setupBlocks = Seq(block(Seq(genesis, issue))) - assertDiffAndState(setupBlocks, block(Seq(sponsor)), s) { case (diff, state) => - diff.sponsorship shouldBe Map(sponsor.asset -> SponsorshipValue(sponsor.minSponsoredAssetFee.get.value)) + assertDiffAndState(setupBlocks, block(Seq(sponsor)), s) { case (snapshot, state) => + snapshot.sponsorships shouldBe Map(sponsor.asset -> SponsorshipValue(sponsor.minSponsoredAssetFee.get.value)) state.assetDescription(sponsor.asset).map(_.sponsorship) shouldBe sponsor.minSponsoredAssetFee.map(_.value) } - assertDiffAndState(setupBlocks, block(Seq(sponsor, sponsor1)), s) { case (diff, state) => - diff.sponsorship shouldBe Map(sponsor.asset -> SponsorshipValue(sponsor1.minSponsoredAssetFee.get.value)) + assertDiffAndState(setupBlocks, block(Seq(sponsor, sponsor1)), s) { case (snapshot, state) => + snapshot.sponsorships shouldBe Map(sponsor.asset -> SponsorshipValue(sponsor1.minSponsoredAssetFee.get.value)) state.assetDescription(sponsor.asset).map(_.sponsorship) shouldBe sponsor1.minSponsoredAssetFee.map(_.value) } - assertDiffAndState(setupBlocks, block(Seq(sponsor, sponsor1, cancel)), s) { case (diff, state) => - diff.sponsorship shouldBe Map(sponsor.asset -> SponsorshipValue(0)) + assertDiffAndState(setupBlocks, block(Seq(sponsor, sponsor1, cancel)), s) { case (snapshot, state) => + snapshot.sponsorships shouldBe Map(sponsor.asset -> SponsorshipValue(0)) state.assetDescription(sponsor.asset).map(_.sponsorship) shouldBe Some(0) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/TransactionValidationErrorPrintTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/TransactionValidationErrorPrintTest.scala index ef67f40a402..eb9982bd253 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/TransactionValidationErrorPrintTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/TransactionValidationErrorPrintTest.scala @@ -53,11 +53,11 @@ class TransactionValidationErrorPrintTest extends PropSpec with Inside with With val untypedScript = Parser.parseExpr(assetScript).get.value - val typedScript = ExprScript(V6, ExpressionCompiler(compilerContext(V6, Expression, isAssetScript = false), untypedScript).explicitGet()._1) + val typedScript = ExprScript(V6, ExpressionCompiler(compilerContext(V6, Expression, isAssetScript = false), V6, untypedScript).explicitGet()._1) .explicitGet() val preTypedScript = - ExprScript(V6, ExpressionCompiler(compilerContext(V6, Expression, isAssetScript = false), Parser.parseExpr("true").get.value).explicitGet()._1) + ExprScript(V6, ExpressionCompiler(compilerContext(V6, Expression, isAssetScript = false), V6, Parser.parseExpr("true").get.value).explicitGet()._1) .explicitGet() val seed = Address.fromString("3MydsP4UeQdGwBq7yDbMvf9MzfB2pxFoUKU").explicitGet() diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/TransferDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/TransferDiffTest.scala index 6273361c430..e8458dfb992 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/TransferDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/TransferDiffTest.scala @@ -2,22 +2,23 @@ package com.wavesplatform.state.diffs import com.wavesplatform.account.Address import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.db.WithState +import com.wavesplatform.db.WithDomain import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.script.v1.ExprScript import com.wavesplatform.lang.v1.compiler.Terms.CONST_BOOLEAN import com.wavesplatform.settings.TestFunctionalitySettings +import com.wavesplatform.state.diffs.FeeValidation.FeeUnit import com.wavesplatform.test.* +import com.wavesplatform.test.DomainPresets.ScriptsAndSponsorship import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.transaction.assets.* import com.wavesplatform.transaction.transfer.* import com.wavesplatform.transaction.{Asset, GenesisTransaction, TxHelpers, TxValidationError, TxVersion} -class TransferDiffTest extends PropSpec with WithState { - - val preconditionsAndTransfer: Seq[(GenesisTransaction, IssueTransaction, IssueTransaction, TransferTransaction)] = { +class TransferDiffTest extends PropSpec with WithDomain { + private val preconditionsAndTransfer = { val master = TxHelpers.signer(1) val recipient = TxHelpers.signer(2) @@ -30,25 +31,32 @@ class TransferDiffTest extends PropSpec with WithState { maybeAsset1 <- Seq(None, Some(issue1.id())).map(Asset.fromCompatId) maybeAsset2 <- Seq(None, Some(issue2.id())).map(Asset.fromCompatId) maybeFeeAsset <- Seq(maybeAsset1, maybeAsset2) + sponsor1 = TxHelpers.sponsor(IssuedAsset(issue1.id()), minSponsoredAssetFee = Some(FeeUnit), sender = master) + sponsor2 = TxHelpers.sponsor(IssuedAsset(issue2.id()), minSponsoredAssetFee = Some(FeeUnit), sender = master) transferV1 = TxHelpers.transfer(master, recipient.toAddress, asset = maybeAsset1, feeAsset = maybeFeeAsset, version = TxVersion.V1) transferV2 = TxHelpers.transfer(master, recipient.toAddress, asset = maybeAsset1, feeAsset = maybeFeeAsset) transfer <- Seq(transferV1, transferV2) - } yield (genesis, issue1, issue2, transfer) + } yield (genesis, sponsor1, sponsor2, issue1, issue2, transfer) } property("transfers assets to recipient preserving waves invariant") { - preconditionsAndTransfer.foreach { case (genesis, issue1, issue2, transfer) => - assertDiffAndState(Seq(TestBlock.create(Seq(genesis, issue1, issue2))), TestBlock.create(Seq(transfer))) { case (totalDiff, newState) => - assertBalanceInvariant(totalDiff) + preconditionsAndTransfer.foreach { case (genesis, sponsor1, sponsor2, issue1, issue2, transfer) => + withDomain(ScriptsAndSponsorship) { d => + d.appendBlock(genesis) + d.appendBlock(issue1, issue2, sponsor1, sponsor2) + d.appendBlock(transfer) + + val carryFee = (sponsor1.fee.value + sponsor2.fee.value + issue1.fee.value + issue2.fee.value - transfer.fee.value) * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, carryFee) - val recipient: Address = transfer.recipient.asInstanceOf[Address] + val recipient = transfer.recipient.asInstanceOf[Address] if (transfer.sender.toAddress != recipient) { transfer.assetId match { case aid @ IssuedAsset(_) => - newState.balance(recipient) shouldBe 0 - newState.balance(recipient, aid) shouldBe transfer.amount.value + d.balance(recipient) shouldBe 0 + d.balance(recipient, aid) shouldBe transfer.amount.value case Waves => - newState.balance(recipient) shouldBe transfer.amount.value + d.balance(recipient) shouldBe transfer.amount.value } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/TransferTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/TransferTransactionDiffTest.scala index 7dcf114daeb..1b7d9dd4b66 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/TransferTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/TransferTransactionDiffTest.scala @@ -1,12 +1,9 @@ package com.wavesplatform.state.diffs -import com.wavesplatform.TestValues -import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.lang.directives.values.V1 import com.wavesplatform.settings.RewardsVotingSettings -import com.wavesplatform.state.{Diff, Portfolio} import com.wavesplatform.test.{NumericExt, PropSpec} import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.transaction.{TxHelpers, TxValidationError, TxVersion} @@ -17,13 +14,12 @@ class TransferTransactionDiffTest extends PropSpec with WithDomain { val sender = TxHelpers.secondAddress val senderKp = TxHelpers.secondSigner val recipient = TxHelpers.address(2) - val feeDiff = Diff(portfolios = Map(sender -> Portfolio.waves(TestValues.fee))) withDomain(DomainPresets.mostRecent.copy(rewardsSettings = RewardsVotingSettings(None)), AddrWithBalance.enoughBalances(senderKp)) { d => val wavesTransfer = TxHelpers.transfer(senderKp, recipient) - assertBalanceInvariant(d.createDiff(wavesTransfer).combineF(feeDiff).explicitGet()) - d.appendAndAssertSucceed(wavesTransfer) + val rewardFee = 6.waves - wavesTransfer.fee.value * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, rewardFee) d.blockchain.balance(recipient) shouldBe wavesTransfer.amount.value d.blockchain.balance(sender) shouldBe ENOUGH_AMT - wavesTransfer.amount.value - wavesTransfer.fee.value } @@ -31,9 +27,9 @@ class TransferTransactionDiffTest extends PropSpec with WithDomain { withDomain(DomainPresets.mostRecent, AddrWithBalance.enoughBalances(senderKp)) { d => val asset = d.helpers.issueAsset(senderKp) val assetTransfer = TxHelpers.transfer(senderKp, recipient, asset = asset, amount = 1000) - assertBalanceInvariant(d.createDiff(assetTransfer).combineF(feeDiff).explicitGet()) - d.appendAndAssertSucceed(assetTransfer) + val rewardAndFee = 6.waves + (1.waves - assetTransfer.fee.value) * 3 / 5 + assertBalanceInvariant(d.liquidSnapshot, d.rocksDBWriter, rewardAndFee) d.blockchain.balance(recipient) shouldBe 0L d.blockchain.balance(recipient, asset) shouldBe 1000L d.blockchain.balance(sender) shouldBe ENOUGH_AMT - assetTransfer.fee.value - 1.waves diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/BigIntInvokeTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/BigIntInvokeTest.scala index f0e177fe71b..f30fc5b9a79 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/BigIntInvokeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/BigIntInvokeTest.scala @@ -147,14 +147,14 @@ class BigIntInvokeTest extends PropSpec with Inside with WithState with DBCacheS d.appendBlock(preparingTxs*) d.appendBlock(invoke) - d.liquidDiff.errorMessage(invoke.id()) shouldBe None - inside(d.liquidDiff.scriptResults.toSeq) { case Seq((_, call1)) => + d.liquidSnapshot.errorMessage(invoke.id()) shouldBe None + inside(d.liquidSnapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call2) => call2.stateChanges.error shouldBe empty call2.stateChanges.invokes shouldBe empty } } - d.liquidDiff.accountData.head._2("key").value shouldBe 1 + d.liquidSnapshot.accountData.head._2("key").value shouldBe 1 } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/CallableV4DiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/CallableV4DiffTest.scala index 3bff19bce34..e4db887802e 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/CallableV4DiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/CallableV4DiffTest.scala @@ -106,7 +106,7 @@ class CallableV4DiffTest extends PropSpec with WithDomain with EitherValues { } } - property("diff contains delete entries") { + property("snapshot contains delete entries") { val deleteEntryDApp = dApp( """ | [ @@ -131,8 +131,8 @@ class CallableV4DiffTest extends PropSpec with WithDomain with EitherValues { Seq(TestBlock.create(genesis :+ setScript)), TestBlock.create(Seq(invoke)), features - ) { case (diff, _) => - diff.accountData(master.toAddress) shouldBe + ) { case (snapshot, _) => + snapshot.accountData(master.toAddress) shouldBe Map( "key1" -> EmptyDataEntry("key1"), "key2" -> EmptyDataEntry("key2") @@ -389,9 +389,9 @@ class CallableV4DiffTest extends PropSpec with WithDomain with EitherValues { Seq(TestBlock.create(genesis :+ setScript)), TestBlock.create(Seq(invoke)), features - ) { case (diff, blockchain) => - val asset = diff.issuedAssets.head._1 - diff.sponsorship shouldBe Map(asset -> SponsorshipValue(minSponsoredAssetFee)) + ) { case (snapshot, blockchain) => + val asset = snapshot.assetStatics.head._1 + snapshot.sponsorships shouldBe Map(asset -> SponsorshipValue(minSponsoredAssetFee)) blockchain.assetDescription(asset).map(_.sponsorship) shouldBe Some(minSponsoredAssetFee) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAffectedAddressTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAffectedAddressTest.scala index 27df11b5e25..5faafa6d041 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAffectedAddressTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAffectedAddressTest.scala @@ -28,7 +28,7 @@ class InvokeAffectedAddressTest extends PropSpec with WithDomain { d.appendAndAssertFailed(invoke(secondAddress)) else d.appendAndAssertSucceed(invoke(secondAddress)) - d.liquidDiff.transactions.head.affected shouldBe Set(defaultAddress, secondAddress) + d.liquidSnapshot.transactions.head._2.affected shouldBe Set(defaultAddress, secondAddress) } } } @@ -42,7 +42,7 @@ class InvokeAffectedAddressTest extends PropSpec with WithDomain { d.appendAndAssertFailed(invoke(Alias.create("alias").explicitGet())) else d.appendAndAssertSucceed(invoke(Alias.create("alias").explicitGet())) - d.liquidDiff.transactions.head.affected shouldBe Set(defaultAddress, secondAddress) + d.liquidSnapshot.transactions.head._2.affected shouldBe Set(defaultAddress, secondAddress) } } } @@ -55,10 +55,10 @@ class InvokeAffectedAddressTest extends PropSpec with WithDomain { d.appendBlock(setScript(secondSigner, dApp(failed))) if (failed) { d.appendBlock(aliasTx, invokeTx) - d.liquidDiff.errorMessage(invokeTx.id()) shouldBe defined + d.liquidSnapshot.errorMessage(invokeTx.id()) shouldBe defined } else d.appendAndAssertSucceed(aliasTx, invokeTx) - d.liquidDiff.transaction(invokeTx.id()).get.affected shouldBe Set(defaultAddress, secondAddress) + d.liquidSnapshot.transactions(invokeTx.id()).affected shouldBe Set(defaultAddress, secondAddress) } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAssetChecksTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAssetChecksTest.scala index c8ecf6b0bd9..46774919df5 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAssetChecksTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeAssetChecksTest.scala @@ -8,11 +8,8 @@ import com.wavesplatform.db.{DBCacheSettings, WithDomain, WithState} import com.wavesplatform.lang.directives.values.{V4, V5} import com.wavesplatform.lang.script.ContractScript.ContractScriptImpl import com.wavesplatform.lang.v1.compiler.TestCompiler -import com.wavesplatform.state.InvokeScriptResult.ErrorMessage -import com.wavesplatform.state.TxMeta.Status -import com.wavesplatform.state.{Diff, InvokeScriptResult, NewTransactionInfo, Portfolio, StateSnapshot} import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} +import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.TxHelpers import com.wavesplatform.transaction.TxHelpers.{invoke, secondSigner, setScript} import org.scalatest.{EitherValues, Inside} @@ -26,7 +23,7 @@ class InvokeAssetChecksTest extends PropSpec with Inside with WithState with DBC private val lengthError = s"Transfer error: invalid asset ID '$invalidLengthAsset' length = 4 bytes, must be 32" private val nonExistentError = s"Transfer error: asset '$nonExistentAsset' is not found on the blockchain" - property("invoke asset checks") { + property("invoke transfer checks") { val dApp = TestCompiler(V4).compileContract( s""" |@Callable(i) @@ -49,71 +46,18 @@ class InvokeAssetChecksTest extends PropSpec with Inside with WithState with DBC activated <- Seq(true, false) func <- Seq("invalidLength", "unexisting") } { - { - val miner = TxHelpers.signer(0).toAddress - val invoker = TxHelpers.signer(1) - val master = TxHelpers.signer(2) - val balances = AddrWithBalance.enoughBalances(invoker, master) - val setScriptTx = TxHelpers.setScript(master, dApp) - val invoke = TxHelpers.invoke(master.toAddress, Some(func), invoker = invoker) - - val dAppAddress = master.toAddress - - def invokeInfo(succeeded: Boolean): Vector[NewTransactionInfo] = - Vector( - NewTransactionInfo( - invoke, - StateSnapshot.empty, - Set(invoke.senderAddress, dAppAddress), - if (succeeded) Status.Succeeded else Status.Failed, - 8 - ) - ) - - val expectedResult = - if (activated) { - val expectingMessage = - if (func == "invalidLength") - lengthError - else - nonExistentError - Diff.withTransactions( - invokeInfo(false), - portfolios = Map( - invoke.senderAddress -> Portfolio(-invoke.fee.value), - miner -> Portfolio((setScriptTx.fee.value * 0.6 + invoke.fee.value * 0.4).toLong + 6.waves) - ), - scriptsComplexity = 8, - scriptResults = Map(invoke.id() -> InvokeScriptResult(error = Some(ErrorMessage(1, expectingMessage)))) - ) - } else { - val asset = if (func == "invalidLength") invalidLengthAsset else nonExistentAsset - Diff.withTransactions( - invokeInfo(true), - portfolios = Map( - invoke.senderAddress -> Portfolio(-invoke.fee.value), - miner -> Portfolio((setScriptTx.fee.value * 0.6 + invoke.fee.value * 0.4).toLong + 6.waves) - ), - scriptsComplexity = 8, - scriptResults = Map( - invoke.id() -> InvokeScriptResult( - transfers = Seq( - InvokeScriptResult.Payment(invoke.senderAddress, Waves, 0), - InvokeScriptResult.Payment(invoke.senderAddress, asset, 0) - ) - ) - ) - ) - } - - def noSnapshot(d: Diff) = - d.copy(transactions = d.transactions.map(_.copy(snapshot = StateSnapshot.empty))) - - withDomain(if (activated) RideV5 else RideV4, balances) { d => - d.appendBlock(setScriptTx) - d.appendBlock(invoke) - noSnapshot(d.liquidDiff) shouldBe expectedResult - } + val invoker = TxHelpers.signer(1) + val master = TxHelpers.signer(2) + val balances = AddrWithBalance.enoughBalances(invoker, master) + val setScriptTx = TxHelpers.setScript(master, dApp) + withDomain(if (activated) RideV5 else RideV4, balances) { d => + d.appendBlock(setScriptTx) + val invoke = TxHelpers.invoke(master.toAddress, Some(func), invoker = invoker) + if (activated) { + val expectingMessage = if (func == "invalidLength") lengthError else nonExistentError + d.appendAndAssertFailed(invoke, expectingMessage) + } else + d.appendAndAssertSucceed(invoke) } } } @@ -270,7 +214,7 @@ class InvokeAssetChecksTest extends PropSpec with Inside with WithState with DBC ) d.appendBlock(setScript(secondSigner, dApp)) d.appendAndAssertSucceed(invoke(fee = invokeFee(issues = 2))) - d.liquidDiff.issuedAssets.size shouldBe 2 + d.liquidSnapshot.assetStatics.size shouldBe 2 } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeFeeTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeFeeTest.scala index c593439d168..92972e2b417 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeFeeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeFeeTest.scala @@ -5,14 +5,11 @@ import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.v1.compiler.TestCompiler -import com.wavesplatform.state.Portfolio import com.wavesplatform.state.diffs.FeeValidation.FeeUnit import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxHelpers.* -import scala.collection.immutable.VectorMap - class InvokeFeeTest extends PropSpec with WithDomain { import DomainPresets.* @@ -73,9 +70,9 @@ class InvokeFeeTest extends PropSpec with WithDomain { val transferTx = transfer(issuer, invoker.toAddress, asset = asset) d.appendBlock(issueTx, sponsorTx, transferTx, setScript(dAppAcc, dApp)) d.appendAndAssertFailed(invoke(invoker = invoker, dApp = dAppAcc.toAddress, fee = invokeFee / coeff, feeAssetId = asset)) - d.liquidDiff.portfolios(invoker.toAddress) shouldBe Portfolio.build(asset, -invokeFee / coeff) - d.liquidDiff.portfolios(issuer.toAddress) shouldBe Portfolio(-invokeFee, assets = VectorMap(asset -> invokeFee / coeff)) - d.liquidDiff.portfolios.get(dAppAcc.toAddress) shouldBe None + d.liquidSnapshot.balances((invoker.toAddress, asset)) shouldBe d.rocksDBWriter.balance(invoker.toAddress, asset) - invokeFee / coeff + d.liquidSnapshot.balances((issuer.toAddress, asset)) shouldBe d.rocksDBWriter.balance(issuer.toAddress, asset) + invokeFee / coeff + d.liquidSnapshot.balances.get((dAppAcc.toAddress, Waves)) shouldBe None } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeReissueTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeReissueTest.scala index 6e9d4358bf7..05af2670086 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeReissueTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeReissueTest.scala @@ -61,7 +61,7 @@ class InvokeReissueTest extends PropSpec with WithDomain { ) d.appendBlock(setScript(secondSigner, dApp)) d.appendBlock(invoke(fee = invokeFee(issues = 1))) - val asset = d.liquidDiff.issuedAssets.head._1 + val asset = d.liquidSnapshot.assetStatics.head._1 d.appendBlock(reissue(asset, secondSigner, 234)) d.blockchain.assetDescription(asset).get.totalVolume shouldBe 1234 } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptActionLimitsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptActionLimitsTest.scala index 6dd756e3aa8..9334726c04d 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptActionLimitsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptActionLimitsTest.scala @@ -57,8 +57,8 @@ class InvokeScriptActionLimitsTest extends PropSpec with WithDomain with DBCache ) assertDiffAndState(Seq(TestBlock.create(preparingTxs1)), TestBlock.create(Seq(invoke1), Block.ProtoBlockVersion), features(version)) { - case (diff, bc) => - diff.scriptResults(invoke1.id()).error shouldBe None + case (snapshot, bc) => + snapshot.scriptResults(invoke1.id()).error shouldBe None bc.accountData(masterAddress, "key") shouldBe Some(IntegerDataEntry("key", 1)) bc.accountData(serviceAddress1, "bar") shouldBe Some(IntegerDataEntry("bar", 1)) } @@ -97,8 +97,8 @@ class InvokeScriptActionLimitsTest extends PropSpec with WithDomain with DBCache ) assertDiffAndState(Seq(TestBlock.create(preparingTxs1)), TestBlock.create(Seq(invoke1), Block.ProtoBlockVersion), features(version)) { - case (diff, bc) => - diff.scriptResults(invoke1.id()).error shouldBe None + case (snapshot, bc) => + snapshot.scriptResults(invoke1.id()).error shouldBe None bc.accountData(masterAddress, "key") shouldBe Some(IntegerDataEntry("key", 1)) bc.accountData(serviceAddress1, "bar") shouldBe Some(IntegerDataEntry("bar", 1)) } @@ -133,8 +133,8 @@ class InvokeScriptActionLimitsTest extends PropSpec with WithDomain with DBCache assetAndDataActionsServiceContract(V6, ContractLimits.MaxAssetScriptActionsAmountV6, ContractLimits.MaxWriteSetSize / 2) ) - assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), features(V6)) { case (diff, bc) => - diff.scriptResults(invoke.id()).error shouldBe None + assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), features(V6)) { case (snapshot, bc) => + snapshot.scriptResults(invoke.id()).error shouldBe None bc.accountData(masterAddress, "key") shouldBe Some(IntegerDataEntry("key", 1)) bc.accountData(serviceAddress, "bar") shouldBe Some(IntegerDataEntry("bar", 1)) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala index c7576526253..fed8004e3c6 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala @@ -64,14 +64,14 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa .foreach(v => withDomain(settingsForRide(v))(assertion(v, _))) private def testDiffTraced(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, from: StdLibVersion = V3, to: StdLibVersion)( - assertion: ((StdLibVersion, TracedResult[ValidationError, Diff])) => Unit + assertion: ((StdLibVersion, TracedResult[ValidationError, StateSnapshot])) => Unit ): Unit = allVersions .filter(v => v >= from && v <= to) .foreach(v => assertDiffEiTraced(preconditions, block, settingsForRide(v).blockchainSettings.functionalitySettings)(r => assertion((v, r)))) private def testDiff(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, from: StdLibVersion = V3, to: StdLibVersion = lastVersion)( - assertion: Either[ValidationError, Diff] => Unit + assertion: Either[ValidationError, StateSnapshot] => Unit ): Unit = testDiffTraced(preconditions, block, from, to)(assertion.compose(_._2.resultE)) @@ -81,7 +81,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa from: StdLibVersion, to: StdLibVersion = lastVersion )( - assertion: (Diff, Blockchain) => Unit + assertion: (StateSnapshot, Blockchain) => Unit ): Unit = allVersions .filter(v => v >= from && v <= to) @@ -462,10 +462,10 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val (_, setScript, ci) = preconditionsAndSetContract(dataContract(version)) d.appendBlock(setScript) d.appendBlock(ci) - d.liquidDiff.scriptsComplexity should be > 0L + d.liquidSnapshot.scriptsComplexity should be > 0L d.blockchain.accountData(dAppAddress, "sender").get.value shouldBe ByteStr(ci.sender.toAddress.bytes) d.blockchain.accountData(dAppAddress, "argument").get.value shouldBe ci.funcCallOpt.get.args.head.asInstanceOf[CONST_BYTESTR].bs - d.liquidDiff.transaction(ci.id()).get.affected.contains(setScript.sender.toAddress) shouldBe true + d.liquidSnapshot.transactions(ci.id()).affected.contains(setScript.sender.toAddress) shouldBe true } ) } @@ -496,10 +496,10 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version)) d.appendBlock(setScript) d.appendBlock(ci) - d.liquidDiff.scriptsComplexity should be > 0L - d.liquidDiff.portfolios(thirdAddress).balance shouldBe amount - d.liquidDiff.portfolios(setScript.sender.toAddress).balance shouldBe -amount - d.liquidDiff.transaction(ci.id()) shouldBe defined + d.liquidSnapshot.scriptsComplexity should be > 0L + d.liquidSnapshot.balances((thirdAddress, Waves)) shouldBe amount + d.liquidSnapshot.balances((setScript.sender.toAddress, Waves)) shouldBe d.rocksDBWriter.balance(setScript.sender.toAddress, Waves) - amount + d.liquidSnapshot.transactions.get(ci.id()) shouldBe defined } ) } @@ -512,10 +512,10 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version)) d.appendBlock(setScript) d.appendBlock(ci) - d.liquidDiff.scriptsComplexity should be > 0L - d.liquidDiff.portfolios(thirdAddress).balance shouldBe amount - d.liquidDiff.portfolios(setScript.sender.toAddress).balance shouldBe -amount - d.liquidDiff.transaction(ci.id()) shouldBe defined + d.liquidSnapshot.scriptsComplexity should be > 0L + d.liquidSnapshot.balances((thirdAddress, Waves)) shouldBe amount + d.liquidSnapshot.balances((setScript.sender.toAddress, Waves)) shouldBe d.rocksDBWriter.balance(setScript.sender.toAddress, Waves) - amount + d.liquidSnapshot.transactions.get(ci.id()) shouldBe defined } ) } @@ -531,8 +531,8 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa from = V4 ) { case (blockDiff, _) => blockDiff.scriptsComplexity should be > 0L - blockDiff.portfolios(thirdAddress) shouldBe Portfolio.waves(amount) - blockDiff.transaction(ci.id()) shouldBe defined + blockDiff.balances((thirdAddress, Waves)) shouldBe ENOUGH_AMT - createAlias.fee.value + amount + blockDiff.transactions.get(ci.id()) shouldBe defined } } @@ -590,7 +590,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => d.appendBlock(setScript, createAlias) d.appendAndAssertSucceed(ci) - d.liquidDiff.scriptsComplexity should be > 0L + d.liquidSnapshot.scriptsComplexity should be > 0L d.balance(thirdAddress, Waves) shouldBe amount d.appendBlockE(fakeCi) should produce("does not exist") } @@ -637,7 +637,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa d.appendBlock(genesis*) d.appendBlock(issue, setScript) d.appendBlock(ci) - inside(d.liquidDiff.scriptResults.toSeq) { case Seq((_, i: InvokeScriptResult)) => + inside(d.liquidSnapshot.scriptResults.toSeq) { case Seq((_, i: InvokeScriptResult)) => i.transfers.size shouldBe 1 } d.blockchain.balance(thirdAddress, Waves) shouldBe amount @@ -686,10 +686,10 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa ) withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => d.appendBlock(asset, setScript) - val tracedDiff = d.transactionDiffer(ci) + val tracedSnapshot = d.transactionDiffer(ci) val message = if (version == V3) "TransactionNotAllowedByScript" else "Transaction is not allowed by script of the asset" - tracedDiff.resultE should produceRejectOrFailedDiff(message) - inside(tracedDiff.trace) { case List(_, AssetVerifierTrace(assetId, Some(tne: TransactionNotAllowedByScript), _)) => + tracedSnapshot.resultE should produceRejectOrFailedDiff(message) + inside(tracedSnapshot.trace) { case List(_, AssetVerifierTrace(assetId, Some(tne: TransactionNotAllowedByScript), _)) => assetId shouldBe asset.id() tne.isAssetScript shouldBe true } @@ -710,7 +710,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa ) d.appendBlock(issue, setScript) d.appendBlock(ci) - d.liquidDiff.scriptsComplexity should be > 0L + d.liquidSnapshot.scriptsComplexity should be > 0L d.balance(dAppAddress, asset) shouldBe (issue.quantity.value - amount) d.balance(thirdAddress, asset) shouldBe amount } @@ -741,9 +741,9 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val (_, setScript, ci) = preconditionsAndSetContract(contract, fee = TestValues.invokeFee(2)) withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => d.appendBlock(issue1, issue2, setScript) - val tracedDiff = d.transactionDiffer(ci) - tracedDiff.resultE should produceRejectOrFailedDiff("Transaction is not allowed by script") - inside(tracedDiff.trace) { + val tracedSnapshot = d.transactionDiffer(ci) + tracedSnapshot.resultE should produceRejectOrFailedDiff("Transaction is not allowed by script") + inside(tracedSnapshot.trace) { case List( InvokeScriptTrace(_, `dAppAddress`, functionCall, Right(scriptResult), _, _), AssetVerifierTrace(allowedAssetId, None, _), @@ -774,9 +774,9 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa ) withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => d.appendBlock(transferringAsset, attachedAsset, setScript) - val tracedDiff = d.transactionDiffer(ci) - tracedDiff.resultE should produceRejectOrFailedDiff(s"Transaction is not allowed by script of the asset ${transferringAsset.id()}") - inside(tracedDiff.trace) { + val tracedSnapshot = d.transactionDiffer(ci) + tracedSnapshot.resultE should produceRejectOrFailedDiff(s"Transaction is not allowed by script of the asset ${transferringAsset.id()}") + inside(tracedSnapshot.trace) { case List( InvokeScriptTrace(_, _, _, Right(scriptResults), _, _), AssetVerifierTrace(transferringAssetId, Some(_), _) @@ -864,8 +864,8 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val t = TxHelpers.transfer(dApp, invokerAddress, sponsorIssue.quantity.value / 2, sponsorAsset) d.appendBlock(sponsorIssue, t, sponsor, setScript) d.appendBlock(ci) - d.liquidDiff.scriptsComplexity should be > 0L - d.liquidDiff.errorMessage(ci.id()) shouldBe None + d.liquidSnapshot.scriptsComplexity should be > 0L + d.liquidSnapshot.errorMessage(ci.id()) shouldBe None d.balance(thirdAddress, Waves) shouldBe amount d.balance(ci.sender.toAddress, sponsorAsset) shouldBe (sponsorIssue.quantity.value / 2 - ci.fee.value) d.balance(dAppAddress, sponsorAsset) shouldBe (sponsorIssue.quantity.value - sponsorIssue.quantity.value / 2 + ci.fee.value) @@ -928,7 +928,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa d.appendBlockE(ci) should produceRejectOrFailedDiff(error) } else { d.appendBlock(ci) - d.liquidDiff.errorMessage(ci.id()).get.text shouldBe error + d.liquidSnapshot.errorMessage(ci.id()).get.text shouldBe error } } } @@ -943,7 +943,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa d.appendBlock(genesis*) d.appendBlock(setScript) d.appendBlock(ci) - d.liquidDiff.errorMessage(ci.id()) shouldBe None + d.liquidSnapshot.errorMessage(ci.id()) shouldBe None } } @@ -956,12 +956,12 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa d.appendBlock(genesis*) if (version == V3) { d.appendBlock(setScript, ci) - d.liquidDiff.errorMessage(ci.id()) shouldBe None + d.liquidSnapshot.errorMessage(ci.id()) shouldBe None } else if (version >= V6) { d.appendBlockE(setScript, ci) should produceRejectOrFailedDiff("Data entry key should not be empty") } else { d.appendBlock(setScript, ci) - d.liquidDiff.errorMessage(ci.id()).map(_.text) shouldBe Some("Data entry key should not be empty") + d.liquidSnapshot.errorMessage(ci.id()).map(_.text) shouldBe Some("Data entry key should not be empty") } } } @@ -1299,15 +1299,15 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa TestCompiler(V4).compileContract(script) } - property("duplicate issuing asset should produce diff error") { + property("duplicate issuing asset should produce snapshot error") { val genesis1Tx = TxHelpers.genesis(dAppAddress) val genesis2Tx = TxHelpers.genesis(invokerAddress) val setScriptTx = TxHelpers.setScript(dApp, doubleIssueContract) val invoke = TxHelpers.invoke(dAppAddress, Some("f"), fee = TestValues.invokeFee(issues = 2)) testDiff(Seq(TestBlock.create(Seq(genesis1Tx, genesis2Tx, setScriptTx))), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), from = V4) { inside(_) { - case Right(diff) => - diff.scriptResults(invoke.id()).error.get.text should include("is already issued") + case Right(snapshot) => + snapshot.scriptResults(invoke.id()).error.get.text should include("is already issued") case Left(TransactionValidationError(InvokeRejectError(error, _), _)) => error should include("is already issued") } } @@ -1326,7 +1326,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa d.appendAndAssertSucceed(ci) ci.feeAssetId shouldBe sponsorAsset ci.dApp shouldBe ci.sender.toAddress - d.liquidDiff.portfolios(ci.sender.toAddress).balanceOf(sponsorAsset) shouldBe 0L + d.liquidSnapshot.balances.get((ci.sender.toAddress, sponsorAsset)) shouldBe None } } } @@ -1384,8 +1384,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), from = V4, to = V5 - ) { case (diff, state) => - diff.portfolios(invoke.sender.toAddress).balanceOf(invoke.feeAssetId) + ) { case (_, state) => state.balance(invoke.sender.toAddress, invoke.feeAssetId) shouldBe invoke.feeAssetId.fold(g2Tx.amount.value)(_ => sponsorIssue.quantity.value ) - invoke.fee.value @@ -1461,11 +1460,11 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa .foreach { arg => val invoke = TxHelpers.invoke(dAppAddress, Some("sameComplexity"), args = List(CONST_STRING(arg).explicitGet())) testDiffAndState(Seq(TestBlock.create(Seq(gTx1, gTx2, ssTx, iTx))), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), from = V4) { - case (diff, _) => + case (snapshot, _) => if (arg == "ok") - diff.errorMessage(invoke.id()) shouldBe empty + snapshot.errorMessage(invoke.id()) shouldBe empty else - diff.errorMessage(invoke.id()) shouldBe defined + snapshot.errorMessage(invoke.id()) shouldBe defined } } @@ -1514,9 +1513,9 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val genesisTxs = Seq(gTx1, gTx2) ++ invokerScriptTx ++ iTxs ++ tTxs ++ saTxs :+ ssTx testDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), from = V4, to = V5) { - case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe defined - diff.scriptsComplexity should be > 0L + case (snapshot, _) => + snapshot.errorMessage(invoke.id()) shouldBe defined + snapshot.scriptsComplexity should be > 0L } testDiff(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), from = V6) { _ should produce("Transaction is not allowed by script of the asset") @@ -1551,8 +1550,8 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments) testDiffAndState(Seq(TestBlock.create(Seq(gTx1, gTx2, alias, ssTx))), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), from = V5) { - case (diff, bc) => - diff.errorMessage(invoke.id()) shouldBe None + case (snapshot, bc) => + snapshot.errorMessage(invoke.id()) shouldBe None val hash = ByteStr(com.wavesplatform.lang.Global.blake2b256(script.bytes().arr)) bc.accountData(dAppAddress, "hash1").get.value shouldBe hash bc.accountData(dAppAddress, "hash2").get.value shouldBe hash diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeValidationTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeValidationTest.scala index 7efbb87d69d..2fd04239c24 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeValidationTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeValidationTest.scala @@ -1,7 +1,6 @@ package com.wavesplatform.state.diffs.ci import com.wavesplatform.TestValues.invokeFee import com.wavesplatform.account.Alias -import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance @@ -125,15 +124,4 @@ class InvokeValidationTest extends PropSpec with WithDomain { PBTransactions.toPBInvokeScriptData(txV2.dApp, txV2.funcCallOpt, txV2.payments).toByteArray.length shouldBe 5120 (the[Exception] thrownBy tooBigTxV2).getMessage shouldBe "GenericError(InvokeScriptTransaction bytes length = 5129 exceeds limit = 5120)" } - - property("unexisting payment asset") { - withDomain(RideV5) { d => - val asset = IssuedAsset(ByteStr.fromBytes(1, 2, 3)) - d.appendBlockE(invoke(defaultAddress, payments = Seq(Payment(1, asset)))) should produce( - "Attempt to transfer unavailable funds: " + - s"Transaction application leads to negative asset '$asset' balance to (at least) temporary negative state, " + - "current balance is 0, spends equals -1, result is -1" - ) - } - } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/LeaseActionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/LeaseActionDiffTest.scala index 3f952dad9ac..3ee74a82db6 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/LeaseActionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/LeaseActionDiffTest.scala @@ -1,5 +1,6 @@ package com.wavesplatform.state.diffs.ci +import cats.Id import com.wavesplatform.account.{Address, Alias} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 @@ -14,11 +15,12 @@ import com.wavesplatform.lang.v1.ContractLimits import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.traits.domain.{Lease, Recipient} import com.wavesplatform.settings.{FunctionalitySettings, TestFunctionalitySettings} +import com.wavesplatform.state.LeaseBalance import com.wavesplatform.state.diffs.FeeValidation.{FeeConstants, FeeUnit} import com.wavesplatform.state.diffs.{ENOUGH_AMT, produceRejectOrFailedDiff} -import com.wavesplatform.state.{LeaseBalance, Portfolio} import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.* +import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.TxHelpers.{defaultAddress, invoke, lease, secondAddress, secondSigner, setScript} import com.wavesplatform.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} import com.wavesplatform.transaction.smart.InvokeScriptTransaction @@ -481,22 +483,20 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.portfolios(invoker) shouldBe Portfolio(-invoke.fee.value, LeaseBalance(leaseAmount, out = 0)) - diff.portfolios(dAppAcc) shouldBe Portfolio(0, LeaseBalance(in = 0, leaseAmount)) + ) { case (snapshot, _) => + snapshot.leaseBalances(invoker) shouldBe LeaseBalance(leaseAmount, out = 0) + snapshot.leaseBalances(dAppAcc) shouldBe LeaseBalance(in = 0, leaseAmount) } } property(s"Lease action cancelled by LeaseCancelTransaction") { val (preparingTxs, invoke, _, dAppAcc, invoker, _, leaseCancelTx) = leasePreconditions(cancelLeaseActionByTx = true) - assertDiffAndState( - Seq(TestBlock.create(preparingTxs)), - TestBlock.create(Seq(invoke, leaseCancelTx)), - v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe empty - diff.portfolios(invoker) shouldBe Portfolio(-invoke.fee.value) - diff.portfolios(dAppAcc) shouldBe Portfolio(-leaseCancelTx.fee.value) + withDomain(RideV5) { d => + d.appendBlock(preparingTxs*) + d.appendBlock(invoke, leaseCancelTx) + d.liquidSnapshot.errorMessage(invoke.id()) shouldBe empty + d.liquidSnapshot.balances((invoker, Waves)) shouldBe d.rocksDBWriter.balance(invoker) - invoke.fee.value + d.liquidSnapshot.balances((dAppAcc, Waves)) shouldBe d.rocksDBWriter.balance(dAppAcc) - leaseCancelTx.fee.value } } @@ -506,8 +506,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "InvalidAddress(Wrong addressBytes length: expected: 26, actual: 0)" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "InvalidAddress(Wrong addressBytes length: expected: 26, actual: 0)" } } @@ -517,8 +517,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "InvalidAddress(Wrong addressBytes length: expected: 26, actual: 10)" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "InvalidAddress(Wrong addressBytes length: expected: 26, actual: 10)" } } @@ -531,8 +531,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "InvalidAddress(Bad address checksum)" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "InvalidAddress(Bad address checksum)" } } @@ -542,8 +542,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "Alias 'alias:T:alias2' does not exists." + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "Alias 'alias:T:alias2' does not exists." } } @@ -553,8 +553,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe s"Alias should contain only following characters: ${Alias.AliasAlphabet}" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe s"Alias should contain only following characters: ${Alias.AliasAlphabet}" } } @@ -564,8 +564,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "NonPositiveAmount(0,waves)" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "NonPositiveAmount(0,waves)" } } @@ -599,8 +599,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe s"Cannot lease more than own: Balance: $dAppBalance, already leased: 0" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe s"Cannot lease more than own: Balance: $dAppBalance, already leased: 0" } } @@ -613,8 +613,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs.toList ::: leaseTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe s"Cannot lease more than own: Balance: ${dAppBalance - leaseFromDApp.fee.value}, already leased: ${leaseFromDApp.amount.value}" } } @@ -627,9 +627,9 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => + ) { case (snapshot, _) => val id = Lease.calculateId(Lease(recipient, amount, nonce = 0), invoke.id()) - diff.errorMessage(invoke.id()).get.text shouldBe s"Lease with id=$id is already in the state" + snapshot.errorMessage(invoke.id()).get.text shouldBe s"Lease with id=$id is already in the state" } } @@ -639,8 +639,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "Cannot lease to self" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "Cannot lease to self" } } @@ -650,8 +650,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "Cannot lease to self" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "Cannot lease to self" } } @@ -677,11 +677,11 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features(version) - ) { case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe empty - diff.portfolios(invoker) shouldBe Portfolio(-invoke.fee.value) - diff.portfolios(dAppAcc) shouldBe Portfolio(lease = LeaseBalance(in = 0, out = amount * limit)) - diff.portfolios(recipient) shouldBe Portfolio(lease = LeaseBalance(in = amount * limit, out = 0)) + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()) shouldBe empty + snapshot.balances((invoker, Waves)) shouldBe ENOUGH_AMT - invoke.fee.value + snapshot.leaseBalances(dAppAcc) shouldBe LeaseBalance(in = 0, out = amount * limit) + snapshot.leaseBalances(recipient) shouldBe LeaseBalance(in = amount * limit, out = 0) } } @@ -749,8 +749,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text should include("is already in the state") + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text should include("is already in the state") } } @@ -763,24 +763,25 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe empty - diff.portfolios(invoker) shouldBe Portfolio(-invoke.fee.value) - diff.portfolios(dAppAcc) shouldBe Portfolio(lease = LeaseBalance(in = 0, out = amount)) - diff.portfolios(recipient) shouldBe Portfolio(lease = LeaseBalance(in = amount, 0)) + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()) shouldBe empty + snapshot.balances((invoker, Waves)) shouldBe ENOUGH_AMT - invoke.fee.value + snapshot.leaseBalances(dAppAcc) shouldBe LeaseBalance(in = 0, out = amount) + snapshot.leaseBalances(recipient) shouldBe LeaseBalance(in = amount, 0) } } property(s"LeaseCancel action for lease performed via LeaseTransaction") { val (preparingTxs, invoke, _, dAppAcc, invoker, leaseTxs, _) = leasePreconditions(useLeaseCancelDApp = true) val leaseFromDApp = leaseTxs.head - assertDiffAndState( - Seq(TestBlock.create(preparingTxs ++ leaseTxs)), - TestBlock.create(Seq(invoke)), - v5Features - ) { case (diff, _) => - diff.portfolios(invoker) shouldBe Portfolio(-invoke.fee.value, LeaseBalance(in = -leaseFromDApp.amount.value, 0)) - diff.portfolios(dAppAcc) shouldBe Portfolio(0, LeaseBalance(0, out = -leaseFromDApp.amount.value)) + withDomain(RideV5) { d => + d.appendBlock(preparingTxs*) + d.appendBlock(leaseTxs*) + d.appendAndAssertSucceed(invoke) + d.liquidSnapshot + .leaseBalances(invoker) shouldBe d.rocksDBWriter.leaseBalance(invoker).combineF[Id](LeaseBalance(in = -leaseFromDApp.amount.value, 0)) + d.liquidSnapshot + .leaseBalances(dAppAcc) shouldBe d.rocksDBWriter.leaseBalance(dAppAcc).combineF[Id](LeaseBalance(0, out = -leaseFromDApp.amount.value)) } } @@ -791,8 +792,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe s"Lease with id=$leaseId not found" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe s"Lease with id=$leaseId not found" } } @@ -803,8 +804,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe s"Lease id=$leaseId has invalid length = 1 byte(s) while expecting 32" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe s"Lease id=$leaseId has invalid length = 1 byte(s) while expecting 32" } } @@ -815,8 +816,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs :+ leaseTx)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text should include("LeaseTransaction was leased by other sender") + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text should include("LeaseTransaction was leased by other sender") } } @@ -828,9 +829,9 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => + ) { case (snapshot, _) => val leaseId = Lease.calculateId(Lease(recipient, amount, nonce = 0), invoke.id()) - diff.errorMessage(invoke.id()).get.text shouldBe s"Duplicate LeaseCancel id(s): $leaseId" + snapshot.errorMessage(invoke.id()).get.text shouldBe s"Duplicate LeaseCancel id(s): $leaseId" } } @@ -840,8 +841,8 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs ++ leaseTxs ++ List(leaseCancelTx))), TestBlock.create(Seq(invoke)), v5Features - ) { case (diff, _) => - diff.errorMessage(invoke.id()).get.text shouldBe "Cannot cancel already cancelled lease" + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()).get.text shouldBe "Cannot cancel already cancelled lease" } } @@ -850,14 +851,18 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { val (preparingTxs, invoke, _, dAppAcc, invoker, ltx, _) = leasePreconditions(useLeaseCancelDApp = true, leaseCancelCount = ContractLimits.MaxCallableActionsAmountBeforeV6(version), version = version) val leaseTxs = ltx.init - assertDiffAndState( - Seq(TestBlock.create(preparingTxs ++ leaseTxs)), - TestBlock.create(Seq(invoke)), - features(version) - ) { case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe empty - diff.portfolios(invoker) shouldBe Portfolio(-invoke.fee.value, LeaseBalance(in = -leaseTxs.map(_.amount.value).sum, out = 0)) - diff.portfolios(dAppAcc) shouldBe Portfolio(0, LeaseBalance(in = 0, out = -leaseTxs.map(_.amount.value).sum)) + withDomain(settingsForRide(version)) { d => + d.appendBlock(preparingTxs*) + d.appendBlock(leaseTxs*) + d.appendAndAssertSucceed(invoke) + d.liquidSnapshot.leaseBalances(invoker) shouldBe + d.rocksDBWriter + .leaseBalance(invoker) + .combineF[Id](LeaseBalance(in = -leaseTxs.map(_.amount.value).sum, out = 0)) + d.liquidSnapshot.leaseBalances(dAppAcc) shouldBe + d.rocksDBWriter + .leaseBalance(dAppAcc) + .combineF[Id](LeaseBalance(in = 0, out = -leaseTxs.map(_.amount.value).sum)) } } @@ -899,11 +904,12 @@ class LeaseActionDiffTest extends PropSpec with WithDomain { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features(version) - ) { case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe empty - diff.portfolios(invoker) shouldBe Portfolio(-invoke.fee.value) - diff.portfolios(dAppAcc) shouldBe Portfolio(-transfersCount, lease = LeaseBalance(in = 0, out = leaseAmount)) - diff.portfolios(recipient) shouldBe Portfolio(transfersCount, lease = LeaseBalance(in = leaseAmount, out = 0)) + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()) shouldBe empty + snapshot.balances((invoker, Waves)) shouldBe ENOUGH_AMT - invoke.fee.value + snapshot.leaseBalances.get(invoker) shouldBe None + snapshot.leaseBalances(dAppAcc) shouldBe LeaseBalance(in = 0, out = leaseAmount) + snapshot.leaseBalances(recipient) shouldBe LeaseBalance(in = leaseAmount, out = 0) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/MultiPaymentInvokeDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/MultiPaymentInvokeDiffTest.scala index ff70c57978b..020e5ca314c 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/MultiPaymentInvokeDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/MultiPaymentInvokeDiffTest.scala @@ -10,11 +10,11 @@ import com.wavesplatform.lang.script.Script import com.wavesplatform.lang.v1.ContractLimits import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.settings.{Constants, TestFunctionalitySettings} -import com.wavesplatform.state.Diff +import com.wavesplatform.state.StateSnapshot import com.wavesplatform.state.TxMeta.Status import com.wavesplatform.state.diffs.* import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.assets.IssueTransaction import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.smart.{InvokeScriptTransaction, SetScriptTransaction} @@ -34,19 +34,14 @@ class MultiPaymentInvokeDiffTest extends PropSpec with WithState { Seq(TestBlock.create(genesis ++ issues ++ Seq(setDApp, setVerifier))), TestBlock.create(Seq(ci)), features - ) { case (diff, blockchain) => - val assetBalance = issues - .map(_.id()) - .map(IssuedAsset(_)) - .map(asset => asset -> blockchain.balance(dAppAcc.toAddress, asset)) - .filter(_._2 != 0) - .toMap - - diff.portfolios(dAppAcc.toAddress).assets shouldBe assetBalance - diff.portfolios(dAppAcc.toAddress).balance shouldBe -wavesTransfer - diff.portfolios(invoker.toAddress).balance shouldBe wavesTransfer - fee + ) { case (snapshot, blockchain) => + issues.foreach { tx => + val asset = IssuedAsset(tx.id()) + blockchain.balance(dAppAcc.toAddress, asset) shouldBe snapshot.balances((dAppAcc.toAddress, asset)) + } + snapshot.balances((dAppAcc.toAddress, Waves)) shouldBe ENOUGH_AMT - setDApp.fee.value - wavesTransfer + snapshot.balances((invoker.toAddress, Waves)) shouldBe ENOUGH_AMT - setVerifier.fee.value - issues.map(_.fee.value).sum - fee + wavesTransfer } - } } @@ -61,15 +56,11 @@ class MultiPaymentInvokeDiffTest extends PropSpec with WithState { Seq(TestBlock.create(genesis ++ issues ++ Seq(setDApp, setVerifier))), TestBlock.create(Seq(ci)), features - ) { case (diff, blockchain) => - val assetBalance = issues - .map(_.id()) - .map(IssuedAsset(_)) - .map(asset => asset -> blockchain.balance(dAppAcc.toAddress, asset)) - .filter(_._2 != 0) - .toMap - - diff.portfolios(dAppAcc.toAddress).assets shouldBe assetBalance + ) { case (snapshot, blockchain) => + issues.foreach { tx => + val asset = IssuedAsset(tx.id()) + blockchain.balance(dAppAcc.toAddress, asset) shouldBe snapshot.balances((dAppAcc.toAddress, asset)) + } } } } @@ -91,7 +82,7 @@ class MultiPaymentInvokeDiffTest extends PropSpec with WithState { TestBlock.create(Seq(ci)), features )(_ should matchPattern { - case Right(diff: Diff) if diff.transactions.exists(_.status != Status.Succeeded) => + case Right(snapshot: StateSnapshot) if snapshot.transactions.exists(_._2.status != Status.Succeeded) => }) } } @@ -145,17 +136,13 @@ class MultiPaymentInvokeDiffTest extends PropSpec with WithState { Seq(TestBlock.create(genesis ++ issues ++ Seq(setDApp, setVerifier))), TestBlock.create(Seq(ci)), features - ) { case (diff, blockchain) => - val assetBalance = issues - .map(_.id()) - .map(IssuedAsset(_)) - .map(asset => asset -> blockchain.balance(dAppAcc.toAddress, asset)) - .filter(_._2 != 0) - .toMap - - diff.portfolios(dAppAcc.toAddress).assets shouldBe assetBalance - diff.portfolios(dAppAcc.toAddress).balance shouldBe -wavesTransfer - diff.portfolios(invoker.toAddress).balance shouldBe wavesTransfer - fee + ) { case (snapshot, blockchain) => + issues.foreach { tx => + val asset = IssuedAsset(tx.id()) + snapshot.balances((dAppAcc.toAddress, asset)) shouldBe blockchain.balance(dAppAcc.toAddress, asset) + } + snapshot.balances((dAppAcc.toAddress, Waves)) shouldBe ENOUGH_AMT - setDApp.fee.value - wavesTransfer + snapshot.balances((invoker.toAddress, Waves)) shouldBe ENOUGH_AMT - setVerifier.fee.value - issues.map(_.fee.value).sum - fee + wavesTransfer } } } @@ -186,8 +173,8 @@ class MultiPaymentInvokeDiffTest extends PropSpec with WithState { ): Seq[ (Seq[GenesisTransaction], SetScriptTransaction, SetScriptTransaction, InvokeScriptTransaction, List[IssueTransaction], KeyPair, KeyPair, Long) ] = { - val master = TxHelpers.signer(0) - val invoker = TxHelpers.signer(1) + val master = TxHelpers.signer(1) + val invoker = TxHelpers.signer(2) val fee = if (withEnoughFee) TxHelpers.ciFee(ContractLimits.MaxAttachedPaymentAmount + 1) else TxHelpers.ciFee(1) @@ -256,8 +243,8 @@ class MultiPaymentInvokeDiffTest extends PropSpec with WithState { TestBlock.create(Seq(ci)), features ) { - case Right(diff: Diff) => - val errMsg = diff.scriptResults(diff.transactions.head.transaction.id()).error.get.text + case Right(snapshot: StateSnapshot) => + val errMsg = snapshot.scriptResults(snapshot.transactions.head._2.transaction.id()).error.get.text message(oldVersion.id, maybeFailedAssetId).r.findFirstIn(errMsg) shouldBe defined case l @ Left(_) => diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala index f204213cb01..2bb47c91b46 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala @@ -8,11 +8,11 @@ import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.script.v1.ExprScript.ExprScriptImpl import com.wavesplatform.lang.v1.compiler.Terms.TRUE import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.state.StringDataEntry import com.wavesplatform.state.TxMeta.Status import com.wavesplatform.state.diffs.FeeValidation.{FeeConstants, FeeUnit} -import com.wavesplatform.state.{Portfolio, StringDataEntry} import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxHelpers.* import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.{TransactionType, TxHelpers} @@ -166,19 +166,20 @@ class RideV5FailRejectTest extends PropSpec with WithDomain { d.appendBlock(setScript(secondSigner, dApp)) d.appendBlock(invokeTx) d.blockchain.transactionInfo(invokeTx.id()).get._1.status == Status.Succeeded shouldBe false - d.liquidDiff.sponsorship shouldBe Map() - d.liquidDiff.leaseState shouldBe Map() - d.liquidDiff.issuedAssets shouldBe Map() - d.liquidDiff.updatedAssets shouldBe Map() - d.liquidDiff.accountData shouldBe Map() + d.liquidSnapshot.sponsorships shouldBe Map() + d.liquidSnapshot.leaseStates shouldBe Map() + d.liquidSnapshot.assetStatics shouldBe Map() + d.liquidSnapshot.assetNamesAndDescriptions shouldBe Map() + d.liquidSnapshot.accountData shouldBe Map() d.blockchain.accountData(secondAddress, "old").get.value shouldBe "value" - d.liquidDiff.portfolios shouldBe { + d.liquidSnapshot.balances shouldBe { val reward = d.blockchain.blockReward(d.blockchain.height).get val setScriptFee = FeeConstants(TransactionType.SetScript) * FeeUnit val previousBlockReward = (0.6 * setScriptFee).toLong val currentBlockReward = (0.4 * invokeFee).toLong - val total = reward + previousBlockReward + currentBlockReward - invokeFee - Map(defaultAddress -> Portfolio.waves(total)) + val balanceDiff = reward + previousBlockReward + currentBlockReward - invokeFee + val dbBalance = d.rocksDBWriter.balance(defaultAddress) + Map((defaultAddress, Waves) -> (dbBalance + balanceDiff)) } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/ScriptTransferTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/ScriptTransferTest.scala index cb2e3920165..aacbfc13383 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/ScriptTransferTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/ScriptTransferTest.scala @@ -90,7 +90,7 @@ class ScriptTransferTest extends PropSpec with WithDomain { val invokeTx = invoke() d.appendBlock(setScript(secondSigner, dApp)) d.appendBlockE(invokeTx) - d.liquidDiff.errorMessage(invokeTx.id()).get.text should include("key not found: asset") + d.liquidSnapshot.errorMessage(invokeTx.id()).get.text should include("key not found: asset") } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/TransactionAssetChecksTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/TransactionAssetChecksTest.scala new file mode 100644 index 00000000000..81317a31b72 --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/TransactionAssetChecksTest.scala @@ -0,0 +1,113 @@ +package com.wavesplatform.state.diffs.ci + +import com.wavesplatform.common.state.ByteStr +import com.wavesplatform.db.WithDomain +import com.wavesplatform.db.WithState.AddrWithBalance +import com.wavesplatform.lang.directives.values.V8 +import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.test.DomainPresets.* +import com.wavesplatform.test.{PropSpec, produce} +import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.EthTxGenerator.{generateEthInvoke, generateEthTransfer} +import com.wavesplatform.transaction.TxHelpers.* +import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment +import com.wavesplatform.transaction.utils.EthConverters.EthereumKeyPairExt + +import scala.util.Try + +class TransactionAssetChecksTest extends PropSpec with WithDomain { + private val dApp = TestCompiler(V8).compileContract( + """ + | @Callable(i) + | func default() = [] + """.stripMargin + ) + private val issueTx = issue(secondSigner) + private val asset = IssuedAsset(issueTx.id()) + + property("invoke script transaction") { + withDomain(TransactionStateSnapshot, AddrWithBalance.enoughBalances(defaultSigner, secondSigner)) { d => + d.appendBlock(setScript(secondSigner, dApp), issueTx) + d.appendBlockE(invoke(secondAddress, payments = Seq(Payment(1, IssuedAsset(ByteStr.fill(31)(1)))))) should produce( + "invalid asset ID 'tVojvhToWjQ8Xvo4UPx2Xz9eRy7auyYMmZBjc2XfN' length = 31 bytes, must be 32" + ) + d.appendBlockE(invoke(secondAddress, payments = Seq(Payment(1, IssuedAsset(ByteStr.fill(33)(1)))))) should produce( + "invalid asset ID 'JJEfe6DcPM2ziB2vfUWDV6aHVerXRGkv3TcyvJUNGHZz' length = 33 bytes, must be 32" + ) + d.appendBlockE(invoke(secondAddress, payments = Seq(Payment(1, IssuedAsset(ByteStr.fill(32)(1)))))) should produce( + "asset '4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi' is not found on the blockchain" + ) + val invokeWithIssued = invoke(secondAddress, payments = Seq(Payment(1, asset))) + d.appendBlockE(invokeWithIssued) should produce(s"leads to negative asset '$asset' balance") + d.appendBlock(transfer(secondSigner, defaultAddress, asset = asset)) + d.appendAndAssertSucceed(invokeWithIssued) + } + } + + property("ethereum invoke script transaction") { + withDomain( + TransactionStateSnapshot, + AddrWithBalance.enoughBalances(defaultSigner, secondSigner) ++ Seq( + AddrWithBalance(defaultSigner.toEthWavesAddress), + AddrWithBalance(secondSigner.toEthWavesAddress) + ) + ) { d => + d.appendBlock(setScript(secondSigner, dApp), issueTx, transfer(secondSigner, defaultSigner.toEthWavesAddress, asset = asset)) + Try( + generateEthInvoke(defaultEthSigner, secondAddress, "default", Nil, Seq(Payment(1, IssuedAsset(ByteStr.fill(31)(1))))) + ).toEither should produce("InvocationTargetException") + Try( + generateEthInvoke(defaultEthSigner, secondAddress, "default", Nil, Seq(Payment(1, IssuedAsset(ByteStr.fill(33)(1))))) + ).toEither should produce("InvocationTargetException") + d.appendBlockE( + generateEthInvoke(defaultEthSigner, secondAddress, "default", Nil, Seq(Payment(1, IssuedAsset(ByteStr.fill(32)(1))))) + ) should produce( + "asset '4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi' is not found on the blockchain" + ) + val invokeWithIssued = generateEthInvoke(secondSigner.toEthKeyPair, secondAddress, "default", Nil, payments = Seq(Payment(1, asset))) + d.appendBlockE(invokeWithIssued) should produce("negative asset balance") + d.appendBlock(transfer(secondSigner, secondSigner.toEthWavesAddress, asset = asset)) + d.appendAndAssertSucceed(invokeWithIssued) + } + } + + property("transfer transaction") { + withDomain(TransactionStateSnapshot, AddrWithBalance.enoughBalances(defaultSigner, secondSigner)) { d => + d.appendBlock(setScript(secondSigner, dApp), issueTx) + d.appendBlockE(transfer(asset = IssuedAsset(ByteStr.fill(31)(1)))) should produce( + "invalid asset ID 'tVojvhToWjQ8Xvo4UPx2Xz9eRy7auyYMmZBjc2XfN' length = 31 bytes, must be 32" + ) + d.appendBlockE(transfer(asset = IssuedAsset(ByteStr.fill(33)(1)))) should produce( + "invalid asset ID 'JJEfe6DcPM2ziB2vfUWDV6aHVerXRGkv3TcyvJUNGHZz' length = 33 bytes, must be 32" + ) + d.appendBlockE(transfer(asset = IssuedAsset(ByteStr.fill(32)(1)))) should produce( + "asset '4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi' is not found on the blockchain" + ) + val transferIssued = transfer(asset = asset) + d.appendBlockE(transferIssued) should produce(s"leads to negative asset '$asset' balance") + d.appendBlock(transfer(secondSigner, defaultAddress, asset = asset)) + d.appendAndAssertSucceed(transferIssued) + } + } + + property("ethereum transfer transaction") { + withDomain( + TransactionStateSnapshot, + AddrWithBalance.enoughBalances(defaultSigner, secondSigner) ++ Seq( + AddrWithBalance(defaultSigner.toEthWavesAddress), + AddrWithBalance(secondSigner.toEthWavesAddress) + ) + ) { d => + d.appendBlock(setScript(secondSigner, dApp), issueTx, transfer(secondSigner, defaultSigner.toEthWavesAddress, asset = asset)) + (31 to 33).foreach(i => + d.appendBlockE(generateEthTransfer(defaultEthSigner, secondAddress, 1, IssuedAsset(ByteStr.fill(i)(1)))) should produce( + "Can't resolve ERC20 address" + ) + ) + val transferIssued = generateEthTransfer(secondSigner.toEthKeyPair, secondAddress, 1, asset) + d.appendBlockE(transferIssued) should produce(s"negative asset balance") + d.appendBlock(transfer(secondSigner, secondSigner.toEthWavesAddress, asset = asset)) + d.appendAndAssertSucceed(transferIssued) + } + } +} diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/InvokeScriptTransactionCrosscontractInvokeDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/InvokeScriptTransactionCrosscontractInvokeDiffTest.scala index ed20ca45b2a..1bee5f15f6b 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/InvokeScriptTransactionCrosscontractInvokeDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/InvokeScriptTransactionCrosscontractInvokeDiffTest.scala @@ -130,8 +130,8 @@ class InvokeScriptTransactionCrosscontractInvokeDiffTest forAll(scenario) { case (genesisTxs, invokeTx, secondDApp) => assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { - case (diff, bc) => - diff.errorMessage(invokeTx.id()) shouldBe None + case (snapshot, bc) => + snapshot.errorMessage(invokeTx.id()) shouldBe None bc.accountData(secondDApp, invokeEntry1Key) shouldBe Some(IntegerDataEntry(invokeEntry1Key, invokeEntry1Val)) bc.accountData(secondDApp, invokeEntry2Key) shouldBe Some(IntegerDataEntry(invokeEntry2Key, invokeEntry2NewVal)) @@ -215,8 +215,8 @@ class InvokeScriptTransactionCrosscontractInvokeDiffTest forAll(scenario) { case (genesisTxs, invokeTx, mainDApp) => assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { - case (diff, bc) => - diff.errorMessage(invokeTx.id()) shouldBe None + case (snapshot, bc) => + snapshot.errorMessage(invokeTx.id()) shouldBe None bc.accountData(mainDApp, invokeEntry1Key) shouldBe Some(IntegerDataEntry(invokeEntry1Key, invokeEntry1Val)) bc.accountData(mainDApp, invokeEntry2Key) shouldBe Some(IntegerDataEntry(invokeEntry2Key, invokeEntry2NewVal)) @@ -396,8 +396,8 @@ class InvokeScriptTransactionCrosscontractInvokeDiffTest forAll(scenario) { case (genesisTxs, invokeTx, thirdAcc, transferAsset, paymentAsset) => assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { - case (diff, bc) => - diff.errorMessage(invokeTx.id()) shouldBe None + case (snapshot, bc) => + snapshot.errorMessage(invokeTx.id()) shouldBe None bc.balance(thirdAcc, IssuedAsset(transferAsset)) shouldBe transferAssetAmount bc.balance(thirdAcc, IssuedAsset(paymentAsset)) shouldBe paymentAssetAmount diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala index f53b4dc645b..4802aa5ec18 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppComplexityCountTest.scala @@ -16,7 +16,7 @@ import com.wavesplatform.state.Portfolio import com.wavesplatform.state.diffs.BlockDiffer.CurrentBlockFeePart import com.wavesplatform.state.diffs.{ENOUGH_AMT, ci} import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.transaction.smart.{InvokeTransaction, SetScriptTransaction} @@ -168,52 +168,56 @@ class SyncDAppComplexityCountTest extends PropSpec with WithDomain { ): Unit = { val (preparingTxs, invokeTx, asset, lastCallingDApp) = scenario(dAppCount, withPayment, withThroughPayment, withThroughTransfer, withVerifier, raiseError, sequentialCalls, invokeExpression) - assertDiffEi( - Seq(TestBlock.create(preparingTxs), TestBlock.create(Seq.empty)), - TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), - features(invokeExpression) - ) { diffE => - if (reject) { - diffE shouldBe Symbol("left") - diffE should produce("Error raised") - } else { - val diff = diffE.explicitGet() - diff.scriptsComplexity shouldBe complexity - if (exceeding) - diff.errorMessage(invokeTx.id()).get.text should include("Invoke complexity limit = 26000 is exceeded") - else if (raiseError) - diff.errorMessage(invokeTx.id()).get.text should include("Error raised") - else - diff.errorMessage(invokeTx.id()) shouldBe None - - val dAppAddress = invokeTx.dApp.asInstanceOf[Address] - val basePortfolios = - Map(TestBlock.defaultSigner.toAddress -> Portfolio(CurrentBlockFeePart(invokeTx.fee.value))) |+| - Map(invokeTx.sender.toAddress -> Portfolio(-invokeTx.fee.value)) - val paymentsPortfolios = - Map(invokeTx.sender.toAddress -> Portfolio.build(asset, -1)) |+| - Map(dAppAddress -> Portfolio.build(asset, 1)) - val throughTransfersPortfolios = - Map(invokeTx.sender.toAddress -> Portfolio.build(asset, 1)) |+| - Map(lastCallingDApp -> Portfolio.build(asset, -1)) - val throughPaymentsPortfolios = - Map(lastCallingDApp -> Portfolio.build(asset, 1)) |+| - Map(dAppAddress -> Portfolio.build(asset, -1)) - - val overlappedPortfolio = Portfolio.build(asset, 0) - val emptyPortfolios = Map.empty[Address, Portfolio] - - val additionalPortfolios = - (if (withPayment) paymentsPortfolios else emptyPortfolios) |+| - (if (withThroughPayment) throughPaymentsPortfolios else emptyPortfolios) |+| - (if (withThroughTransfer) throughTransfersPortfolios else emptyPortfolios) - - val totalPortfolios = if (exceeding || raiseError) basePortfolios else basePortfolios |+| additionalPortfolios - - diff.portfolios.filter(_._2 != overlappedPortfolio) shouldBe - totalPortfolios - .filter(_._2 != overlappedPortfolio) - .map(p => p.copy(_2 = p._2.copy(assets = p._2.assets.filterNot(_._2 == 0)))) + withTestState(features(invokeExpression)) { (bu, db) => + assertDiffEi( + Seq(TestBlock.create(preparingTxs), TestBlock.create(Seq.empty)), + TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), + bu, + db, + enableExecutionLog = false + ) { diffE => + if (reject) { + diffE shouldBe Symbol("left") + diffE should produce("Error raised") + } else { + val snapshot = diffE.explicitGet() + snapshot.scriptsComplexity shouldBe complexity + if (exceeding) + snapshot.errorMessage(invokeTx.id()).get.text should include("Invoke complexity limit = 26000 is exceeded") + else if (raiseError) + snapshot.errorMessage(invokeTx.id()).get.text should include("Error raised") + else + snapshot.errorMessage(invokeTx.id()) shouldBe None + + val dAppAddress = invokeTx.dApp.asInstanceOf[Address] + val basePortfolios = + Map(TestBlock.defaultSigner.toAddress -> Portfolio(CurrentBlockFeePart(invokeTx.fee.value))) |+| + Map(invokeTx.sender.toAddress -> Portfolio(-invokeTx.fee.value)) + val paymentsPortfolios = + Map(invokeTx.sender.toAddress -> Portfolio.build(asset, -1)) |+| + Map(dAppAddress -> Portfolio.build(asset, 1)) + val throughTransfersPortfolios = + Map(invokeTx.sender.toAddress -> Portfolio.build(asset, 1)) |+| + Map(lastCallingDApp -> Portfolio.build(asset, -1)) + val throughPaymentsPortfolios = + Map(lastCallingDApp -> Portfolio.build(asset, 1)) |+| + Map(dAppAddress -> Portfolio.build(asset, -1)) + + val emptyPortfolios = Map.empty[Address, Portfolio] + val additionalPortfolios = + (if (withPayment) paymentsPortfolios else emptyPortfolios) |+| + (if (withThroughPayment) throughPaymentsPortfolios else emptyPortfolios) |+| + (if (withThroughTransfer) throughTransfersPortfolios else emptyPortfolios) + + val expectedPortfolios = if (exceeding || raiseError) basePortfolios else basePortfolios |+| additionalPortfolios + expectedPortfolios + .foreach { case (address, expectedPortfolio) => + expectedPortfolio.balance shouldBe snapshot.balances.get((address, Waves)).map(_ - db.balance(address)).getOrElse(0) + expectedPortfolio.assets.foreach { case (asset, balance) => + balance shouldBe snapshot.balances.get((address, asset)).map(_ - db.balance(address, asset)).getOrElse(0) + } + } + } } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppGeneratingBalanceTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppGeneratingBalanceTest.scala index 07aebadeb4e..492982797ae 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppGeneratingBalanceTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppGeneratingBalanceTest.scala @@ -35,10 +35,10 @@ class SyncDAppGeneratingBalanceTest extends PropSpec with WithDomain { d.appendBlock(setScript(defaultSigner, dApp), setScript(secondSigner, dApp)) d.appendAndAssertSucceed(invoke(secondAddress, invoker = secondSigner)) - d.liquidDiff.accountData.head._2.head._2.value shouldBe 0 + d.liquidSnapshot.accountData.head._2.head._2.value shouldBe 0 d.appendAndAssertSucceed(invoke(secondAddress, invoker = secondSigner)) - d.liquidDiff.accountData.head._2.head._2.value shouldBe amount + d.liquidSnapshot.accountData.head._2.head._2.value shouldBe amount } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLeaseBalanceCheckTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLeaseBalanceCheckTest.scala index eab3c890818..fdbba4048ec 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLeaseBalanceCheckTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLeaseBalanceCheckTest.scala @@ -70,7 +70,7 @@ class SyncDAppLeaseBalanceCheckTest extends PropSpec with WithDomain { if (bigComplexityDApp1 || bigComplexityDApp2) { d.appendBlock(invoke) - d.liquidDiff.errorMessage(invoke.txId).get.text should include("Cannot lease more than own: Balance: 0") + d.liquidSnapshot.errorMessage(invoke.txId).get.text should include("Cannot lease more than own: Balance: 0") } else { d.appendBlockE(invoke) should produce("Cannot lease more than own: Balance: 0") } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLimits.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLimits.scala index 3777f2dccb4..27b4ee28c4f 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLimits.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppLimits.scala @@ -64,8 +64,8 @@ class SyncDAppLimits extends PropSpec with WithDomain with OptionValues with Eit val calls = complexityLimit / 2000 + 1 val invokeTx = TxHelpers.invoke(aliceAddr, Some("foo"), Seq(CONST_LONG(calls)), invoker = alice) - val diff = d.createDiffE(invokeTx).value - val (_, scriptResult) = diff.scriptResults.headOption.value + val snapshot = d.createDiffE(invokeTx).value + val (_, scriptResult) = snapshot.scriptResults.headOption.value scriptResult.error.value.text should include(s"Invoke complexity limit = $complexityLimit is exceeded") d.appendBlock(invokeTx) @@ -99,8 +99,8 @@ class SyncDAppLimits extends PropSpec with WithDomain with OptionValues with Eit val invokeTx = TxHelpers.invoke(aliceAddr, Some("foo"), Seq(CONST_LONG(101)), invoker = alice) - val diff = d.createDiffE(invokeTx).value - val (_, scriptResult) = diff.scriptResults.headOption.value + val snapshot = d.createDiffE(invokeTx).value + val (_, scriptResult) = snapshot.scriptResults.headOption.value scriptResult.error.value.text should include("DApp calls limit = 100 is exceeded") d.appendBlock(invokeTx) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppNegativeIssueTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppNegativeIssueTest.scala index bce5c824764..4377f2733c8 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppNegativeIssueTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppNegativeIssueTest.scala @@ -67,7 +67,7 @@ class SyncDAppNegativeIssueTest extends PropSpec with WithDomain { d.appendBlock(preparingTxs*) if (bigComplexityDApp1 || bigComplexityDApp2) { d.appendBlock(invoke) - d.liquidDiff.errorMessage(invoke.txId).get.text should include("Invalid decimals") + d.liquidSnapshot.errorMessage(invoke.txId).get.text should include("Invalid decimals") } else { d.appendBlockE(invoke) should produce("Invalid decimals") d.appendBlock() diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppPaymentTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppPaymentTest.scala index ce4064f7c02..d2be554298c 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppPaymentTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppPaymentTest.scala @@ -10,7 +10,6 @@ import com.wavesplatform.lang.directives.values.V5 import com.wavesplatform.lang.script.Script import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames -import com.wavesplatform.state.Portfolio import com.wavesplatform.state.diffs.{ENOUGH_AMT, produceRejectOrFailedDiff} import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.* @@ -34,8 +33,8 @@ class SyncDAppPaymentTest extends PropSpec with WithDomain { d.appendBlock(invoke1) d.blockchain.transactionSucceeded(invoke1.id.value()) shouldBe true - d.liquidDiff.portfolios(dApp1) shouldBe Portfolio.build(asset, 1) - d.liquidDiff.portfolios(dApp2) shouldBe Portfolio.build(asset, -1) + d.liquidSnapshot.balances((dApp1, asset)) shouldBe d.rocksDBWriter.balance(dApp1, asset) + 1 + d.liquidSnapshot.balances((dApp2, asset)) shouldBe d.rocksDBWriter.balance(dApp2, asset) - 1 val invoke2 = invoke() d.appendBlockE(invoke2) should produce { diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala index 32a135f3f4e..eea40a4817c 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppRecursionTest.scala @@ -109,9 +109,9 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features(invokeExpression) - ) { case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe None - inside(diff.scriptResults.toSeq) { case Seq((_, call1)) => + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id()) shouldBe None + inside(snapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call2) => inside(call2.stateChanges.invokes) { case Seq(call3) => call3.stateChanges.error shouldBe empty @@ -308,9 +308,9 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features() - ) { case (diff, _) => - diff.errorMessage(invoke.id.value()) shouldBe None - inside(diff.scriptResults.toSeq) { case Seq((_, call1)) => + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id.value()) shouldBe None + inside(snapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call2) => inside(call2.stateChanges.invokes) { case Seq(call3) => inside(call3.stateChanges.invokes) { case Seq(call4) => @@ -352,9 +352,9 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features() - ) { case (diff, _) => - diff.errorMessage(invoke.id.value()) shouldBe None - inside(diff.scriptResults.toSeq) { case Seq((_, call1)) => + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id.value()) shouldBe None + inside(snapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call2) => inside(call2.stateChanges.invokes) { case Seq(call3) => inside(call3.stateChanges.invokes) { case Seq(call4) => @@ -394,9 +394,9 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features() - ) { case (diff, _) => - diff.errorMessage(invoke.id.value()) shouldBe None - inside(diff.scriptResults.toSeq) { case Seq((_, call1)) => + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id.value()) shouldBe None + inside(snapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call2) => inside(call2.stateChanges.invokes) { case Seq(call3) => inside(call3.stateChanges.invokes) { case Seq(call4) => @@ -476,9 +476,9 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features() - ) { case (diff, _) => - diff.errorMessage(invoke.id.value()) shouldBe None - inside(diff.scriptResults.toSeq) { case Seq((_, call1)) => + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id.value()) shouldBe None + inside(snapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call2) => inside(call2.stateChanges.invokes) { case Seq(call3) => inside(call3.stateChanges.invokes) { case Seq(call4) => @@ -565,9 +565,9 @@ class SyncDAppRecursionTest extends PropSpec with WithDomain with Inside { Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invoke)), features() - ) { case (diff, _) => - diff.errorMessage(invoke.id.value()) shouldBe None - inside(diff.scriptResults.toSeq) { case Seq((_, call1)) => + ) { case (snapshot, _) => + snapshot.errorMessage(invoke.id.value()) shouldBe None + inside(snapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call21, call22) => inside(call21.stateChanges.invokes) { case Seq(call31) => call31.stateChanges.error shouldBe empty diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppReissueTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppReissueTest.scala index 665ceabed83..ba46aeb05bf 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppReissueTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppReissueTest.scala @@ -102,7 +102,7 @@ class SyncDAppReissueTest extends PropSpec with WithDomain { if (bigComplexityDApp1 || bigComplexityDApp2) { d.appendBlock(invoke) - d.liquidDiff.errorMessage(invoke.txId).get.text should include("Asset was issued by other address") + d.liquidSnapshot.errorMessage(invoke.txId).get.text should include("Asset was issued by other address") } else { d.appendBlockE(invoke) should produce("Asset was issued by other address") } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppTransferTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppTransferTest.scala index 9aa6f375b8f..c393500a034 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppTransferTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncDAppTransferTest.scala @@ -18,7 +18,7 @@ import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.evaluator.FunctionIds.CREATE_LIST import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames import com.wavesplatform.protobuf.dapp.DAppMeta -import com.wavesplatform.state.diffs.produceRejectOrFailedDiff +import com.wavesplatform.state.diffs.{ENOUGH_AMT, produceRejectOrFailedDiff} import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} @@ -108,10 +108,10 @@ class SyncDAppTransferTest extends PropSpec with WithDomain with Inside { } property("invoking ScriptTransfer in sync call results in accounts state") { - val invoker = TxHelpers.signer(0) - val invokerDApp = TxHelpers.signer(1) - val senderDApp = TxHelpers.signer(2) - val recipient = TxHelpers.signer(3) + val invoker = TxHelpers.signer(1) + val invokerDApp = TxHelpers.signer(2) + val senderDApp = TxHelpers.signer(3) + val recipient = TxHelpers.signer(4) val transferAmount = 10.waves @@ -131,17 +131,17 @@ class SyncDAppTransferTest extends PropSpec with WithDomain with Inside { call2.stateChanges.invokes shouldBe empty } } - blockDiff.portfolios(recipient.toAddress).balance shouldBe transferAmount - blockDiff.portfolios(senderDApp.toAddress).balance shouldBe -transferAmount - blockDiff.transaction(invoke.id()) shouldBe defined + blockDiff.balances((recipient.toAddress, Waves)) shouldBe transferAmount + blockDiff.balances((senderDApp.toAddress, Waves)) shouldBe ENOUGH_AMT - setSenderScript.fee.value - transferAmount + blockDiff.transactions.get(invoke.id()) shouldBe defined } } property("invoking default func ScriptTransfer in sync call results in accounts state") { - val invoker = TxHelpers.signer(0) - val invokerDApp = TxHelpers.signer(1) - val senderDApp = TxHelpers.signer(2) - val recipient = TxHelpers.signer(3) + val invoker = TxHelpers.signer(1) + val invokerDApp = TxHelpers.signer(2) + val senderDApp = TxHelpers.signer(3) + val recipient = TxHelpers.signer(4) val transferAmount = 10.waves @@ -161,9 +161,9 @@ class SyncDAppTransferTest extends PropSpec with WithDomain with Inside { call2.stateChanges.invokes shouldBe empty } } - blockDiff.portfolios(recipient.toAddress).balance shouldBe transferAmount - blockDiff.portfolios(senderDApp.toAddress).balance shouldBe -transferAmount - blockDiff.transaction(invoke.id()) shouldBe defined + blockDiff.balances((recipient.toAddress, Waves)) shouldBe transferAmount + blockDiff.balances((senderDApp.toAddress, Waves)) shouldBe ENOUGH_AMT - setSenderScript.fee.value - transferAmount + blockDiff.transactions.get(invoke.id()) shouldBe defined } } @@ -184,10 +184,10 @@ class SyncDAppTransferTest extends PropSpec with WithDomain with Inside { } property("ScriptTransfer in sync call is allowed if funds were received from attached payment") { - val invoker = TxHelpers.signer(0) - val invokerDApp = TxHelpers.signer(1) - val senderDApp = TxHelpers.signer(2) - val recipient = TxHelpers.signer(3) + val invoker = TxHelpers.signer(1) + val invokerDApp = TxHelpers.signer(2) + val senderDApp = TxHelpers.signer(3) + val recipient = TxHelpers.signer(4) val transferAmount = 10.waves @@ -210,9 +210,9 @@ class SyncDAppTransferTest extends PropSpec with WithDomain with Inside { call2.stateChanges.invokes shouldBe empty } } - blockDiff.portfolios(recipient.toAddress).balance shouldBe transferAmount - blockDiff.portfolios(invokerDApp.toAddress).balance shouldBe -transferAmount - blockDiff.transaction(invoke.id()) shouldBe defined + blockDiff.balances((recipient.toAddress, Waves)) shouldBe transferAmount + blockDiff.balances((invokerDApp.toAddress, Waves)) shouldBe ENOUGH_AMT - setSenderScript.fee.value - transferAmount + blockDiff.transactions.get(invoke.id()) shouldBe defined } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala index 40eb6e81083..e573d9caf04 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeActionsTest.scala @@ -3,13 +3,14 @@ package com.wavesplatform.state.diffs.ci.sync import com.wavesplatform.TestValues.invokeFee import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance -import com.wavesplatform.lang.directives.values._ +import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.test.{PropSpec, produce} -import com.wavesplatform.transaction.TxHelpers._ +import com.wavesplatform.transaction.Asset.Waves +import com.wavesplatform.transaction.TxHelpers.* class SyncInvokeActionsTest extends PropSpec with WithDomain { - import DomainPresets._ + import DomainPresets.* private val dApp1Signer = secondSigner private val dApp1Address = secondAddress @@ -68,9 +69,9 @@ class SyncInvokeActionsTest extends PropSpec with WithDomain { ) d.appendBlock(setScript(dApp1Signer, dApp1), setScript(dApp2Signer, dApp2)) d.appendAndAssertSucceed(invoke(dApp1Address, fee = invokeFee(issues = 1))) - d.liquidDiff.portfolios.get(dApp1Address) shouldBe empty - d.liquidDiff.portfolios.get(dApp2Address) shouldBe empty - d.liquidDiff.portfolios(defaultAddress).assets.head._2 shouldBe 1000 + d.liquidSnapshot.balances.get((dApp1Address, Waves)) shouldBe empty + d.liquidSnapshot.balances.get((dApp2Address, Waves)) shouldBe empty + d.liquidSnapshot.balances.collect { case ((address, asset), amount) if address == defaultAddress && asset != Waves => amount}.head shouldBe 1000 } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala index 6fdfbfc872e..040ce701672 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeDiffTest.scala @@ -68,8 +68,8 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), Nil, payments) assertDiffAndState(Seq(TestBlock.create(Seq(gTx1, gTx2, ssTx))), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { - case (diff, bc) => - diff.errorMessage(invoke.id()) shouldBe None + case (snapshot, bc) => + snapshot.errorMessage(invoke.id()) shouldBe None bc.accountData(dAppAddress, "key") shouldBe Some(IntegerDataEntry("key", 1)) bc.accountData(dAppAddress, "bar") shouldBe Some(IntegerDataEntry("bar", 1)) } @@ -109,10 +109,10 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), Nil, payments) assertDiffAndState(Seq(TestBlock.create(Seq(gTx1, gTx2, ssTx))), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { - case (diff, _) => - diff.errorMessage(invoke.id()) shouldBe None - diff.scriptsComplexity shouldBe 108 - inside(diff.scriptResults.toSeq) { case Seq((_, call1)) => + case (snapshot, _) => + snapshot.errorMessage(invoke.id()) shouldBe None + snapshot.scriptsComplexity shouldBe 108 + inside(snapshot.scriptResults.toSeq) { case Seq((_, call1)) => inside(call1.invokes) { case Seq(call2) => call2.stateChanges.error shouldBe empty call2.stateChanges.invokes shouldBe empty @@ -187,11 +187,11 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), Nil, payments, fee = TestValues.invokeFee(issues = 1)) val genesisTxs = Seq(gTx1, gTx2, gTx3, aliasTx, ssTx1, ssTx) - assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => - diff.scriptResults(invoke.id()).error shouldBe None - val l = diff.scriptResults(invoke.id()).leases(0) - val l1 = diff.scriptResults(invoke.id()).leases(1) - val l2 = diff.scriptResults(invoke.id()).leaseCancels(0) + assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => + snapshot.scriptResults(invoke.id()).error shouldBe None + val l = snapshot.scriptResults(invoke.id()).leases(0) + val l1 = snapshot.scriptResults(invoke.id()).leases(1) + val l2 = snapshot.scriptResults(invoke.id()).leaseCancels(0) l.amount shouldBe 13 l.recipient shouldBe thirdAcc.toAddress l1.amount shouldBe 23 @@ -266,11 +266,11 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments, fee = TestValues.invokeFee(issues = 1)) val genesisTxs = Seq(gTx1, gTx2, gTx3, aliasTx, ssTx1, ssTx) - assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => - diff.scriptResults(invoke.id()).error shouldBe None - val l = diff.scriptResults(invoke.id()).leases(0) - val l1 = diff.scriptResults(invoke.id()).leases(1) - val l2 = diff.scriptResults(invoke.id()).leaseCancels(0) + assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => + snapshot.scriptResults(invoke.id()).error shouldBe None + val l = snapshot.scriptResults(invoke.id()).leases(0) + val l1 = snapshot.scriptResults(invoke.id()).leases(1) + val l2 = snapshot.scriptResults(invoke.id()).leaseCancels(0) l.amount shouldBe 13 l.recipient shouldBe thirdAcc.toAddress l1.amount shouldBe 23 @@ -340,8 +340,8 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val payments = List(Payment(10L, Waves)) val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments, fee = TestValues.invokeFee(2)) val genesisTxs = Seq(gTx1, gTx2, gTx3, aliasTx, ssTx1, ssTx) - assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => - val err = diff.scriptResults(invoke.id()).error.get + assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => + val err = snapshot.scriptResults(invoke.id()).error.get err.code shouldBe FailedTransactionError.Cause.FeeForActions.code bc.accountData(dAppAddress, "key") shouldBe None bc.accountData(thirdAcc.toAddress, "bar") shouldBe None @@ -409,7 +409,7 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments, fee = TestValues.invokeFee(issues = 1)) val genesisTxs = Seq(gTx1, gTx2, gTx3, aliasTx, ssTx1, ssTx) - assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => + assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => bc.accountData(dAppAddress, "key") shouldBe Some(IntegerDataEntry("key", 1)) bc.accountData(thirdAcc.toAddress, "bar") shouldBe Some(IntegerDataEntry("bar", 1)) } @@ -481,7 +481,7 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val genesisTxs = Seq(gTx1, gTx2, gTx3, ssTx1, ssTx) - assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => + assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => bc.accountData(dAppAddress, "key") shouldBe Some(IntegerDataEntry("key", 1)) bc.accountData(thirdAcc.toAddress, "bar") shouldBe Some(IntegerDataEntry("bar", 1)) } @@ -1064,8 +1064,8 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invoke = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments, fee = fee) val genesisTxs = Seq(gTx1, gTx2, gTx3, setServiceDApp, setClientDApp, paymentIssue, transferIssue) - assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => - diff.errorMessage(invoke.id()) shouldBe None + assertDiffAndState(Seq(TestBlock.create(genesisTxs)), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => + snapshot.errorMessage(invoke.id()) shouldBe None bc.accountData(dAppAddress, "key") shouldBe Some(IntegerDataEntry("key", 1)) bc.accountData(thirdAcc.toAddress, "bar") shouldBe Some(IntegerDataEntry("bar", 1)) @@ -1137,8 +1137,8 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invokeTx = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments) val preparingTxs = Seq(gTx1, gTx2, gTx3, ssTxMain, ssTxSecond, dataTxSecond, dataTxSecond2) - assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => - diff.errorMessage(invokeTx.id()) shouldBe None + assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => + snapshot.errorMessage(invokeTx.id()) shouldBe None bc.accountData(thirdAddress, invokeEntry1Key) shouldBe Some(IntegerDataEntry(invokeEntry1Key, invokeEntry1Val)) bc.accountData(thirdAddress, invokeEntry2Key) shouldBe Some(IntegerDataEntry(invokeEntry2Key, invokeEntry2NewVal)) } @@ -1196,8 +1196,8 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invokeTx = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments) val preparingTxs = Seq(gTx1, gTx2, ssTxMain, dataTxMain, dataTxMain2) - assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => - diff.errorMessage(invokeTx.id()) shouldBe None + assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => + snapshot.errorMessage(invokeTx.id()) shouldBe None bc.accountData(dAppAddress, invokeEntry1Key) shouldBe Some(IntegerDataEntry(invokeEntry1Key, invokeEntry1Val)) bc.accountData(dAppAddress, invokeEntry2Key) shouldBe Some(IntegerDataEntry(invokeEntry2Key, invokeEntry2NewVal)) } @@ -1300,8 +1300,8 @@ class SyncInvokeDiffTest extends PropSpec with WithDomain with DBCacheSettings w val invokeTx = TxHelpers.invoke(dAppAddress, Some("foo"), payments = payments) val preparingTxs = Seq(gTx1, gTx2, gTx3, gTx4, ssTxMain, ssTxSecond, ssTxThird, paymentIssue, transferIssue) - assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { case (diff, bc) => - diff.errorMessage(invokeTx.id()) shouldBe None + assertDiffAndState(Seq(TestBlock.create(preparingTxs)), TestBlock.create(Seq(invokeTx), Block.ProtoBlockVersion), fsWithV5) { case (snapshot, bc) => + snapshot.errorMessage(invokeTx.id()) shouldBe None bc.balance(thirdAcc.toAddress, IssuedAsset(transferIssue.id())) shouldBe transferAssetAmount bc.balance(thirdAcc.toAddress, IssuedAsset(paymentIssue.id())) shouldBe paymentAssetAmount } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeFailAndRejectTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeFailAndRejectTest.scala index 305aeee3481..3b5dd17e357 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeFailAndRejectTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeFailAndRejectTest.scala @@ -7,11 +7,11 @@ import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.script.v1.ExprScript.ExprScriptImpl import com.wavesplatform.lang.v1.compiler.Terms.TRUE import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.state.StringDataEntry import com.wavesplatform.state.TxMeta.Status import com.wavesplatform.state.diffs.FeeValidation.{FeeConstants, FeeUnit} -import com.wavesplatform.state.{Portfolio, StringDataEntry} import com.wavesplatform.test.{PropSpec, produce} -import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TransactionType import com.wavesplatform.transaction.TxHelpers.* import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment @@ -76,19 +76,21 @@ class SyncInvokeFailAndRejectTest extends PropSpec with WithDomain { d.appendBlock(setScript(dApp2Signer, dApp2)) d.appendBlock(invokeTx) d.blockchain.transactionInfo(invokeTx.id()).get._1.status == Status.Succeeded shouldBe false - d.liquidDiff.sponsorship shouldBe Map() - d.liquidDiff.leaseState shouldBe Map() - d.liquidDiff.issuedAssets shouldBe Map() - d.liquidDiff.updatedAssets shouldBe Map() - d.liquidDiff.accountData shouldBe Map() + d.liquidSnapshot.sponsorships shouldBe Map() + d.liquidSnapshot.leaseStates shouldBe Map() + d.liquidSnapshot.assetStatics shouldBe Map() + d.liquidSnapshot.assetNamesAndDescriptions shouldBe Map() + d.liquidSnapshot.assetVolumes shouldBe Map() + d.liquidSnapshot.accountData shouldBe Map() d.blockchain.accountData(dApp2Address, "old").get.value shouldBe "value" - d.liquidDiff.portfolios shouldBe { + d.liquidSnapshot.balances shouldBe { val reward = d.blockchain.blockReward(d.blockchain.height).get val setScriptFee = FeeConstants(TransactionType.SetScript) * FeeUnit val previousBlockReward = (0.6 * setScriptFee).toLong val currentBlockReward = (0.4 * invokeFee).toLong - val total = reward + previousBlockReward + currentBlockReward - invokeFee - Map(defaultAddress -> Portfolio.waves(total)) + val balanceDiff = reward + previousBlockReward + currentBlockReward - invokeFee + val dbBalance = d.rocksDBWriter.balance(defaultAddress) + Map((defaultAddress, Waves) -> (balanceDiff + dbBalance)) } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala index f5a2165bf29..7f1f0695582 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala @@ -42,11 +42,11 @@ class SyncInvokeLeaseTest extends PropSpec with WithDomain { withDomain(RideV5, AddrWithBalance.enoughBalances(dApp1Signer)) { d => d.appendBlock(setScript(dApp1Signer, twoLeaseDApp(555))) d.appendAndAssertSucceed(invoke(dApp1Address)) - val lease1 = d.liquidDiff.leaseState.head._2 + val lease1 = d.liquidSnapshot.leaseStates.head._2 lease1.status shouldBe a[Cancelled] lease1.recipient shouldBe dApp2Address lease1.amount shouldBe 1 - val lease2 = d.liquidDiff.leaseState.last._2 + val lease2 = d.liquidSnapshot.leaseStates.last._2 lease2.status shouldBe Active lease2.recipient shouldBe dApp2Address lease2.amount shouldBe 555 @@ -57,7 +57,7 @@ class SyncInvokeLeaseTest extends PropSpec with WithDomain { withDomain(RideV5, AddrWithBalance.enoughBalances(dApp1Signer)) { d => d.appendBlock(setScript(dApp1Signer, twoLeaseDApp(1))) d.appendAndAssertFailed(invoke(dApp1Address), "already in the state") - d.liquidDiff.leaseState shouldBe Map() + d.liquidSnapshot.leaseStates shouldBe Map() } } @@ -83,7 +83,7 @@ class SyncInvokeLeaseTest extends PropSpec with WithDomain { ) d.appendBlock(setScript(dApp1Signer, dApp)) d.appendAndAssertFailed(invoke(dApp1Address), "Cannot cancel already cancelled lease") - d.liquidDiff.leaseState shouldBe Map() + d.liquidSnapshot.leaseStates shouldBe Map() } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeTotalPaymentsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeTotalPaymentsTest.scala index a3b0ae863da..58c6c383dd0 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeTotalPaymentsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeTotalPaymentsTest.scala @@ -75,7 +75,7 @@ class SyncInvokeTotalPaymentsTest extends PropSpec with WithDomain { if (error) if (fail) { d.appendAndAssertFailed(tx) - d.liquidDiff.errorMessage(tx.id()).get.text should include("Invoke payments limit = 100 is exceeded") + d.liquidSnapshot.errorMessage(tx.id()).get.text should include("Invoke payments limit = 100 is exceeded") } else d.appendBlockE(tx) should produce("Invoke payments limit = 100 is exceeded") else diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeValidationTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeValidationTest.scala index 7b0559ec41a..bff3d5b6ff7 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeValidationTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeValidationTest.scala @@ -228,7 +228,7 @@ class SyncInvokeValidationTest extends PropSpec with WithDomain { d.appendBlock(setScript(dApp1Signer, dApp1), setScript(dApp2Signer, dApp2), setScript(dApp3Signer, dApp3)) d.appendAndAssertSucceed(invoke(dApp1Address)) - d.liquidDiff.transactions.head.affected shouldBe Set(dApp1Address, dApp2Address, dApp3Address, defaultAddress) + d.liquidSnapshot.transactions.head._2.affected shouldBe Set(dApp1Address, dApp2Address, dApp3Address, defaultAddress) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/freecall/InvokeExpressionTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/freecall/InvokeExpressionTest.scala index c74dc25897b..fad4021826c 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/freecall/InvokeExpressionTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/freecall/InvokeExpressionTest.scala @@ -10,10 +10,12 @@ import com.wavesplatform.lang.script.Script import com.wavesplatform.lang.script.v1.ExprScript import com.wavesplatform.lang.v1.ContractLimits import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.protobuf.ByteStringExt +import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot.AssetStatic import com.wavesplatform.state.diffs.FeeValidation.{FeeConstants, FeeUnit} import com.wavesplatform.state.diffs.ci.ciFee import com.wavesplatform.state.diffs.{ENOUGH_AMT, FeeValidation} -import com.wavesplatform.state.{BinaryDataEntry, BooleanDataEntry, NewAssetInfo} +import com.wavesplatform.state.{AssetInfo, AssetVolumeInfo, BinaryDataEntry, BooleanDataEntry} import com.wavesplatform.test.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.assets.{IssueTransaction, SponsorFeeTransaction} @@ -156,8 +158,8 @@ class InvokeExpressionTest extends PropSpec with ScalaCheckPropertyChecks with W d.appendBlock(invoke) d.blockchain.accountData(invoke.sender.toAddress, "check").get shouldBe BooleanDataEntry("check", true) d.blockchain.accountData(invoke.sender.toAddress, "transactionId").get shouldBe BinaryDataEntry("transactionId", invoke.txId) - d.liquidDiff.issuedAssets.size shouldBe 1 - checkAsset(invoke, d.liquidDiff.issuedAssets.head._2) + d.liquidSnapshot.assetStatics.size shouldBe 1 + checkAsset(invoke, d.liquidSnapshot.assetStatics.head._2, d.liquidSnapshot.assetNamesAndDescriptions.head._2, d.liquidSnapshot.assetVolumes.head._2) } } @@ -324,21 +326,23 @@ class InvokeExpressionTest extends PropSpec with ScalaCheckPropertyChecks with W withDomain(ContinuationTransaction) { d => d.appendBlock(genesisTxs*) d.appendBlock(invoke) - d.liquidDiff.errorMessage(invoke.id.value()).get.text should include("Explicit script termination") + d.liquidSnapshot.errorMessage(invoke.id.value()).get.text should include("Explicit script termination") } } private[this] def checkAsset( invoke: InvokeExpressionTransaction, - asset: NewAssetInfo + static: AssetStatic, + info: AssetInfo, + volume: AssetVolumeInfo ): Assertion = { - asset.dynamic.name.toStringUtf8 shouldBe TestAssetName - asset.dynamic.description.toStringUtf8 shouldBe TestAssetDesc - asset.volume.volume shouldBe TestAssetVolume - asset.volume.isReissuable shouldBe TestAssetReissuable - asset.static.decimals shouldBe TestAssetDecimals - asset.static.nft shouldBe false - asset.static.issuer shouldBe invoke.sender + info.name.toStringUtf8 shouldBe TestAssetName + info.description.toStringUtf8 shouldBe TestAssetDesc + volume.volume shouldBe TestAssetVolume + volume.isReissuable shouldBe TestAssetReissuable + static.decimals shouldBe TestAssetDecimals + static.nft shouldBe false + static.issuerPublicKey.toPublicKey shouldBe invoke.sender } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/package.scala b/node/src/test/scala/com/wavesplatform/state/diffs/package.scala index 2b4c14025d4..cbb712e2a95 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/package.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/package.scala @@ -3,5 +3,5 @@ package com.wavesplatform.state package object diffs { val ENOUGH_AMT: Long = Long.MaxValue / 3 - def produceRejectOrFailedDiff(errorMessage: String, requireFailed: Boolean = false): DiffProduceError = new DiffProduceError(errorMessage, requireFailed) + def produceRejectOrFailedDiff(errorMessage: String, requireFailed: Boolean = false): SnapshotProduceError = new SnapshotProduceError(errorMessage, requireFailed) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/DiffComplexityCountTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/DiffComplexityCountTest.scala index ef7d4936d51..e37b5749635 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/DiffComplexityCountTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/DiffComplexityCountTest.scala @@ -21,7 +21,8 @@ class DiffComplexityCountTest extends PropSpec with Inside with WithState with D private val activationHeight = 4 - private val fsWithV5 = TestFunctionalitySettings.Enabled.copy(preActivatedFeatures = Map( + private val fsWithV5 = TestFunctionalitySettings.Enabled.copy( + preActivatedFeatures = Map( BlockchainFeatures.SmartAccounts.id -> 0, BlockchainFeatures.SmartAssets.id -> 0, BlockchainFeatures.Ride4DApps.id -> 0, @@ -30,7 +31,9 @@ class DiffComplexityCountTest extends PropSpec with Inside with WithState with D BlockchainFeatures.BlockReward.id -> 0, BlockchainFeatures.BlockV5.id -> 0, BlockchainFeatures.SynchronousCalls.id -> activationHeight - ), estimatorPreCheckHeight = Int.MaxValue) + ), + estimatorPreCheckHeight = Int.MaxValue + ) // ~1900 complexity val groth: String = @@ -57,23 +60,23 @@ class DiffComplexityCountTest extends PropSpec with Inside with WithState with D private def dApp(asset: IssuedAsset): Script = TestCompiler(V4).compileContract( s""" - | {-# STDLIB_VERSION 4 #-} - | {-# CONTENT_TYPE DAPP #-} - | {-# SCRIPT_TYPE ACCOUNT #-} - | - | @Callable(i) - | func default() = { - | strict cond = - | if (true) - | then true - | else ($groth) - | - | [ - | ScriptTransfer(i.caller, 1, base58'$asset'), - | Burn(base58'$asset', 1), - | Reissue(base58'$asset', 1, true) - | ] - | } + | {-# STDLIB_VERSION 4 #-} + | {-# CONTENT_TYPE DAPP #-} + | {-# SCRIPT_TYPE ACCOUNT #-} + | + | @Callable(i) + | func default() = { + | strict cond = + | if (true) + | then true + | else ($groth) + | + | [ + | ScriptTransfer(i.caller, 1, base58'$asset'), + | Burn(base58'$asset', 1), + | Reissue(base58'$asset', 1, true) + | ] + | } """.stripMargin ) @@ -95,15 +98,15 @@ class DiffComplexityCountTest extends PropSpec with Inside with WithState with D (balances, Seq(issue, transfer1, setVerifier, setDApp), invokeFromScripted) } - property(s"evaluated complexity is used for diff instead of estimated one after activation ${BlockchainFeatures.SynchronousCalls}") { + property(s"evaluated complexity is used instead of estimated one after activation ${BlockchainFeatures.SynchronousCalls}") { val (balances, preparingTxs, invoke) = paymentPreconditions withDomain(domainSettingsWithFS(fsWithV5), balances) { d => d.appendBlock(preparingTxs*) val invoke1 = invoke() d.appendBlock(invoke1) - d.liquidDiff.errorMessage(invoke1.id()) shouldBe empty - d.liquidDiff.scriptsComplexity shouldBe 11459 // 3 actions + 2 payments + verifier = 6 * 1900 = 11400 + d.liquidSnapshot.errorMessage(invoke1.id()) shouldBe empty + d.liquidSnapshot.scriptsComplexity shouldBe 11459 // 3 actions + 2 payments + verifier = 6 * 1900 = 11400 // for dApp evaluated complexity is always used after implementation of the snapshots d.appendBlock() @@ -111,8 +114,8 @@ class DiffComplexityCountTest extends PropSpec with Inside with WithState with D val invoke2 = invoke() d.appendBlock(invoke2) - d.liquidDiff.errorMessage(invoke2.id()) shouldBe empty - d.liquidDiff.scriptsComplexity shouldBe 17 + d.liquidSnapshot.errorMessage(invoke2.id()) shouldBe empty + d.liquidSnapshot.scriptsComplexity shouldBe 17 } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/EstimationSwitchTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/EstimationSwitchTest.scala index ed6e94e50cb..3b2ee6609cd 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/EstimationSwitchTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/EstimationSwitchTest.scala @@ -45,13 +45,13 @@ class EstimationSwitchTest extends PropSpec with WithDomain with TransactionGenB d.appendBlock(genesis1, genesis2) d.appendBlock(setScript(), invoke()) - d.liquidDiff.scripts.head._2.get.complexitiesByEstimator(3)("default") shouldBe 5 - d.liquidDiff.scriptsComplexity shouldBe 7 + d.liquidSnapshot.accountScripts.head._2.get.complexitiesByEstimator(3)("default") shouldBe 5 + d.liquidSnapshot.scriptsComplexity shouldBe 7 // bigger than estimator because of ignoring predefined user function complexities d.appendBlock(setScript(), invoke()) - d.liquidDiff.scripts.head._2.get.complexitiesByEstimator(3)("default") shouldBe 1 - d.liquidDiff.scriptsComplexity shouldBe 1 + d.liquidSnapshot.accountScripts.head._2.get.complexitiesByEstimator(3)("default") shouldBe 1 + d.liquidSnapshot.scriptsComplexity shouldBe 1 // condition decreased by 1, // accessing to ref ([] = nil) decreased by 1, // != decreased by 4 (because of using predefined user function complexities) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/MaxCallableComplexityTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/MaxCallableComplexityTest.scala index 4af4a6af511..025bcd8807e 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/MaxCallableComplexityTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/MaxCallableComplexityTest.scala @@ -41,8 +41,8 @@ class MaxCallableComplexityTest extends PropSpec with WithDomain with Transactio val setLargeScript = SetScriptTransaction.selfSigned(TxVersion.V2, dApp, Some(largeScript(V6, 300)), 0.022.waves, ts).explicitGet() d.appendBlock(genDApp, genInvoker, setScript) - val invokeDiff = d.transactionDiffer(invokeScript(invoker, dApp.toAddress, "test")).resultE.explicitGet() - invokeDiff.scriptsComplexity shouldBe 51585 + val invokeSnapshot = d.transactionDiffer(invokeScript(invoker, dApp.toAddress, "test")).resultE.explicitGet() + invokeSnapshot.scriptsComplexity shouldBe 51585 d.appendAndCatchError(setLargeScript).toString should include("Contract function (test) is too complex: 54301 > 52000") } } @@ -61,8 +61,8 @@ class MaxCallableComplexityTest extends PropSpec with WithDomain with Transactio val setInvokeScript = SetScriptTransaction.selfSigned(TxVersion.V2, invokeDApp, Some(invokeScript(V5, largeDApp.toAddress)), 0.01.waves, ts).explicitGet() d.appendBlock(genInvoker, genLargeDApp, genInvokeDApp, setLargeScript, setInvokeScript) - val invokeDiff = d.transactionDiffer(invokeScript(invoker, invokeDApp.toAddress, "invokeTest")).resultE.explicitGet() - invokeDiff.scriptsComplexity shouldBe 18177 + val invokeSnapshot = d.transactionDiffer(invokeScript(invoker, invokeDApp.toAddress, "invokeTest")).resultE.explicitGet() + invokeSnapshot.scriptsComplexity shouldBe 18177 } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAccountFeeTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAccountFeeTest.scala index df1a4d5b85f..6aeebc0de3f 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAccountFeeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAccountFeeTest.scala @@ -136,8 +136,8 @@ class SmartAccountFeeTest extends PropSpec with WithDomain { notEnoughPaidVerifierTxs.foreach(tx => appendAndAssertNotEnoughFee(tx(), d)) d.appendAndAssertSucceed(freeVerifierTxs.map(_())*) - d.liquidDiff.scriptsComplexity should be > 0L - d.liquidDiff.scriptResults.size shouldBe 2 + d.liquidSnapshot.scriptsComplexity should be > 0L + d.liquidSnapshot.scriptResults.size shouldBe 2 } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAssetEvalTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAssetEvalTest.scala index 4fa3b295680..2c3cea3ec0e 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAssetEvalTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/SmartAssetEvalTest.scala @@ -31,7 +31,7 @@ class SmartAssetEvalTest extends PropSpec with WithState { """.stripMargin val parsedEmptyScript = Parser.parseExpr(emptyScript).get.value val emptyExprScript = - ExprScript(V3, ExpressionCompiler(compilerContext(V3, Expression, isAssetScript = true), parsedEmptyScript).explicitGet()._1) + ExprScript(V3, ExpressionCompiler(compilerContext(V3, Expression, isAssetScript = true), V3, parsedEmptyScript).explicitGet()._1) .explicitGet() val issue = TxHelpers.issue(firstAcc, 100, script = Some(emptyExprScript), reissuable = false) val asset = IssuedAsset(issue.id()) @@ -50,7 +50,7 @@ class SmartAssetEvalTest extends PropSpec with WithState { | """.stripMargin val untypedScript = Parser.parseExpr(assetScript).get.value - val typedScript = ExprScript(V3, ExpressionCompiler(compilerContext(V3, Expression, isAssetScript = true), untypedScript).explicitGet()._1) + val typedScript = ExprScript(V3, ExpressionCompiler(compilerContext(V3, Expression, isAssetScript = true), V3, untypedScript).explicitGet()._1) .explicitGet() val setAssetScript = TxHelpers.setAssetScript(firstAcc, asset, typedScript) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala index 482414a7f50..ca0ce7c2cb7 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala @@ -13,7 +13,6 @@ import com.wavesplatform.lang.script.v1.ExprScript import com.wavesplatform.lang.v1.compiler.{Terms, TestCompiler} import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames import com.wavesplatform.lang.v1.traits.domain.AttachedPayments.* -import com.wavesplatform.state.Portfolio import com.wavesplatform.state.diffs.ENOUGH_AMT import com.wavesplatform.state.diffs.ci.ciFee import com.wavesplatform.state.diffs.smart.predef.{assertProvenPart, provenPart} @@ -150,14 +149,19 @@ class EthereumInvokeTest extends PropSpec with WithDomain with EthHelpers with I d.appendBlock(preparingTxs*) d.appendBlock(ethInvoke) - d.liquidDiff.errorMessage(ethInvoke.id()) shouldBe None - d.liquidDiff.accountData(dApp)("check").value shouldBe true - if (syncCall) d.liquidDiff.accountData(dApp2)("check").value shouldBe true + d.liquidSnapshot.errorMessage(ethInvoke.id()) shouldBe None + d.liquidSnapshot.accountData(dApp)("check").value shouldBe true + if (syncCall) d.liquidSnapshot.accountData(dApp2)("check").value shouldBe true - val assetsPortfolio = assets.map(Portfolio.build(_, paymentAmount)).fold(Portfolio())((p1, p2) => p1.combine(p2).explicitGet()) - d.liquidDiff.portfolios.getOrElse(dApp, Portfolio()) shouldBe assetsPortfolio - d.liquidDiff.portfolios(ethInvoke.senderAddress()) shouldBe Portfolio(-ethInvoke.underlying.getGasPrice.longValue()).minus(assetsPortfolio) - inside(d.liquidDiff.scriptResults.toSeq) { case Seq((_, call1)) => + val ethSender = ethInvoke.senderAddress() + assets.foreach { asset => + d.liquidSnapshot.balances((dApp, asset)) shouldBe d.rocksDBWriter.balance(dApp, asset) + paymentAmount + d.liquidSnapshot.balances((ethSender, asset)) shouldBe d.rocksDBWriter.balance(ethSender, asset) - paymentAmount + } + d.liquidSnapshot.balances.get((dApp, Waves)) shouldBe None + d.liquidSnapshot.balances((ethSender, Waves)) shouldBe d.rocksDBWriter.balance(ethSender) - ethInvoke.underlying.getGasPrice.longValue() + + inside(d.liquidSnapshot.scriptResults.toSeq) { case Seq((_, call1)) => if (syncCall) inside(call1.invokes) { case Seq(call2) => call2.stateChanges.error shouldBe empty diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumTransferSmartTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumTransferSmartTest.scala index 42e07a22471..1964204c44f 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumTransferSmartTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumTransferSmartTest.scala @@ -10,7 +10,6 @@ import com.wavesplatform.lang.directives.DirectiveDictionary import com.wavesplatform.lang.directives.values.{StdLibVersion, V3, V6} import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames -import com.wavesplatform.state.Portfolio import com.wavesplatform.state.diffs.ENOUGH_AMT import com.wavesplatform.state.diffs.smart.predef.{assertProvenPart, provenPart} import com.wavesplatform.test.* @@ -21,7 +20,6 @@ import com.wavesplatform.transaction.transfer.TransferTransaction import com.wavesplatform.transaction.{Asset, ERC20Address, EthTxGenerator, EthereumTransaction, TxHelpers} import com.wavesplatform.utils.EthHelpers -import scala.collection.immutable.VectorMap import scala.math.Ordering.Implicits.infixOrderingOps class EthereumTransferSmartTest extends PropSpec with WithDomain with EthHelpers { @@ -95,15 +93,21 @@ class EthereumTransferSmartTest extends PropSpec with WithDomain with EthHelpers d.appendBlock(setVerifier()) d.appendBlock(ProtoBlockVersion, ethTransfer) - val transferPortfolio = Portfolio.build(asset, transferAmount) - d.liquidDiff.portfolios(recipient.toAddress) shouldBe transferPortfolio - d.liquidDiff.portfolios(ethSender) shouldBe Portfolio.waves(-ethTransfer.underlying.getGasLimit.longValue()).minus(transferPortfolio) + d.liquidSnapshot.balances((recipient.toAddress, asset)) shouldBe d.rocksDBWriter.balance(recipient.toAddress, asset) + transferAmount + if (asset == Waves) + d.liquidSnapshot.balances((ethSender, Waves)) shouldBe + d.rocksDBWriter.balance(ethSender, Waves) - ethTransfer.underlying.getGasLimit.longValue() - transferAmount + else { + d.liquidSnapshot.balances((ethSender, Waves)) shouldBe + d.rocksDBWriter.balance(ethSender, Waves) - ethTransfer.underlying.getGasLimit.longValue() + d.liquidSnapshot.balances((ethSender, asset)) shouldBe d.rocksDBWriter.balance(ethSender, asset) - transferAmount + } d.appendBlock() if (version >= V6) { d.appendBlock(setVerifier()) // just for account script execution - d.liquidDiff.scriptsComplexity should be > 0L + d.liquidSnapshot.scriptsComplexity should be > 0L } else if (version >= V3) { (the[Exception] thrownBy d.appendBlock(setVerifier())).getMessage should include( "value() called on unit value on function 'transferTransactionById' call" @@ -137,14 +141,14 @@ class EthereumTransferSmartTest extends PropSpec with WithDomain with EthHelpers d.appendBlock(issue, preTransfer) d.appendBlock(ProtoBlockVersion, ethTransfer) - d.liquidDiff.errorMessage(ethTransfer.id()) shouldBe None - d.liquidDiff.portfolios(recipient.toAddress) shouldBe Portfolio.build(asset, transferAmount) - d.liquidDiff.portfolios(ethTransfer.senderAddress()) shouldBe Portfolio( - -ethTransfer.underlying.getGasPrice.longValue(), - assets = VectorMap(asset -> -transferAmount) - ) + d.liquidSnapshot.errorMessage(ethTransfer.id()) shouldBe None + d.liquidSnapshot.scriptsComplexity should be > 0L - d.liquidDiff.scriptsComplexity should be > 0L + d.liquidSnapshot.balances((recipient.toAddress, asset)) shouldBe transferAmount + d.liquidSnapshot.balances((ethTransfer.senderAddress(), Waves)) shouldBe + d.rocksDBWriter.balance(ethTransfer.senderAddress(), Waves) - ethTransfer.underlying.getGasPrice.longValue() + d.liquidSnapshot.balances((ethTransfer.senderAddress(), asset)) shouldBe + d.rocksDBWriter.balance(ethTransfer.senderAddress(), asset) - transferAmount } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/performance/SigVerifyPerformanceTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/performance/SigVerifyPerformanceTest.scala index 1d72d4f469b..72a48d31fcd 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/performance/SigVerifyPerformanceTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/performance/SigVerifyPerformanceTest.scala @@ -34,7 +34,7 @@ class SigVerifyPerformanceTest extends PropSpec with WithState { ignore("parallel native signature verification vs sequential scripted signature verification") { val textScript = "sigVerify(tx.bodyBytes,tx.proofs[0],tx.senderPk)" val untypedScript = Parser.parseExpr(textScript).get.value - val typedScript = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedScript).explicitGet()._1 + val typedScript = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untypedScript).explicitGet()._1 val (gen, setScript, transfers, scriptTransfers) = differentTransfers(typedScript) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CalculateDelayTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CalculateDelayTest.scala index c9df847c920..af47ac69af1 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CalculateDelayTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CalculateDelayTest.scala @@ -50,7 +50,7 @@ class CalculateDelayTest extends PropSpec with WithDomain { val minDelays = (1 to 200).map { _ => d.appendBlock(invoke()) - d.liquidDiff.accountData(secondAddress).values.minBy(_.value.asInstanceOf[Long]).key + d.liquidSnapshot.accountData(secondAddress).values.minBy(_.value.asInstanceOf[Long]).key } val lowestIsMiner = minDelays.count(_ == "lowest") val mediumIsMiner = minDelays.count(_ == "medium") diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ObsoleteTransactionBindingsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ObsoleteTransactionBindingsTest.scala index f34cf5f73d9..dd3690471ac 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ObsoleteTransactionBindingsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ObsoleteTransactionBindingsTest.scala @@ -74,7 +74,7 @@ class ObsoleteTransactionBindingsTest extends PropSpec with WithState { recipients.map { recipient => val payment = TxHelpers.payment(master, recipient.toAddress, ENOUGH_AMT * 2) val untypedScript = Parser.parseExpr(script(genesis, payment)).get.value - val typedScript = ExprScript(ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedScript).explicitGet()._1) + val typedScript = ExprScript(ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untypedScript).explicitGet()._1) .explicitGet() val setScriptTransaction = TxHelpers.setScript(recipient, typedScript) val nextTransfer = TxHelpers.transfer(recipient, master.toAddress) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideCreateMerkleRootTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideCreateMerkleRootTest.scala index 98b1f992c85..1288d0837a0 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideCreateMerkleRootTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideCreateMerkleRootTest.scala @@ -42,7 +42,7 @@ class RideCreateMerkleRootTest extends PropSpec with WithDomain { val invokeTx = invoke(func = Some("merkle"), args = Seq(digests, id, index)) d.appendBlock(d.createBlock(blockVersion, Seq(invokeTx))) - d.liquidDiff.scriptResults.head._2.error shouldBe None + d.liquidSnapshot.scriptResults.head._2.error shouldBe None d.blockchain.accountData(secondAddress, "root").get.value shouldBe root } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ScriptVersionsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ScriptVersionsTest.scala index 0b52b3541c8..445763153b3 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ScriptVersionsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ScriptVersionsTest.scala @@ -30,7 +30,7 @@ class ScriptVersionsTest extends FreeSpec { ): Either[String, EVALUATED] = { val expr = Parser.parseExpr(script).get.value for { - compileResult <- ExpressionCompiler(compilerContext(version, Expression, isAssetScript = false), expr) + compileResult <- ExpressionCompiler(compilerContext(version, Expression, isAssetScript = false), version, expr) (typedExpr, _) = compileResult s <- ExprScript(version, typedExpr, checkSize = false) r <- eval(s, tx, blockchain) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/SerContextFunctionsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/SerContextFunctionsTest.scala index e905d11339b..2799e788f6f 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/SerContextFunctionsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/SerContextFunctionsTest.scala @@ -49,7 +49,7 @@ class SerContextFunctionsTest extends PropSpec { ) val untypedScript = Parser.parseExpr(scriptWithAllV1Functions(dtx, ttx)).get.value - val compiledScript = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedScript).explicitGet()._1 + val compiledScript = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untypedScript).explicitGet()._1 val bytes = Base64.decode("BAAAAANybmQJAAAAAAAAAgkAAGoAAAACCAUAAAACdHgAAAAJdGltZXN0YW1wAAAAAAAAAAACAAAAAAAAAAAABAAAAAdsb25nQWxsAwMDAwkAAAAAAAACCQAAaAAAAAIAAAAAAAAAA+gAAAAAAAAAAAIAAAAAAAAAB9AJAAAAAAAAAgkAAGkAAAACAAAAAAAAAAPoAAAAAAAAAAACAAAAAAAAAAH0BwkAAAAAAAACCQAAagAAAAIAAAAAAAAAA+gAAAAAAAAAAAIAAAAAAAAAAAAHCQAAAAAAAAIJAABkAAAAAgAAAAAAAAAD6AAAAAAAAAAAAgAAAAAAAAAD6gcJAAAAAAAAAgkAAGUAAAACAAAAAAAAAAPoAAAAAAAAAAACAAAAAAAAAAPmBwQAAAAJc3VtU3RyaW5nCQAAAAAAAAIJAAEsAAAAAgkAASwAAAACAgAAAAJoYQIAAAABLQIAAAACaGECAAAABWhhLWhhBAAAAA1zdW1CeXRlVmVjdG9yBAAAAAckbWF0Y2gwBQAAAAJ0eAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAPRGF0YVRyYW5zYWN0aW9uBAAAAAJkMAUAAAAHJG1hdGNoMAQAAAAEYm9keQgFAAAAAmQwAAAACWJvZHlCeXRlcwkAAAAAAAACCQAAywAAAAIFAAAABGJvZHkBAAAAZAwB1SiqvsNcoQDYfHt6EoYy+vGc1EUxgZRXRFEToyoh7yIABAADaW50AAAAAAAAAAAYAARib29sAQEABGJsb2ICAAVhbGljZQADc3RyAwAEdGVzdAAAAWODBPoKAAAAAAABhqAJAADLAAAAAgEAAABkDAHVKKq+w1yhANh8e3oShjL68ZzURTGBlFdEUROjKiHvIgAEAANpbnQAAAAAAAAAABgABGJvb2wBAQAEYmxvYgIABWFsaWNlAANzdHIDAAR0ZXN0AAABY4ME+goAAAAAAAGGoAEAAABkDAHVKKq+w1yhANh8e3oShjL68ZzURTGBlFdEUROjKiHvIgAEAANpbnQAAAAAAAAAABgABGJvb2wBAQAEYmxvYgIABWFsaWNlAANzdHIDAAR0ZXN0AAABY4ME+goAAAAAAAGGoAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAATVHJhbnNmZXJUcmFuc2FjdGlvbgYHBAAAAAdlcVVuaW9uBAAAAAckbWF0Y2gwBQAAAAJ0eAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAPRGF0YVRyYW5zYWN0aW9uBgMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAATVHJhbnNmZXJUcmFuc2FjdGlvbgQAAAACdDAFAAAAByRtYXRjaDAJAAAAAAAAAggFAAAAAnQwAAAACXJlY2lwaWVudAkBAAAAB0FkZHJlc3MAAAABAQAAABoBVGQXjd+A7J8+39ZW6Opokqso6ed67rqcnQcEAAAABWJhc2ljAwMDBQAAAAdsb25nQWxsBQAAAAlzdW1TdHJpbmcHBQAAAA1zdW1CeXRlVmVjdG9yBwUAAAAHZXFVbmlvbgcEAAAABm5lUHJpbQMDCQEAAAACIT0AAAACAAAAAAAAAAPoAAAAAAAAAAPnCQEAAAACIT0AAAACCQABLAAAAAICAAAAAmhhAgAAAAJoYQIAAAAFaGEtaGEHCQEAAAACIT0AAAACCAUAAAACdHgAAAAJYm9keUJ5dGVzAQAAAASFqFqFBwQAAAAYbmVEYXRhRW50cnlBbmRHZXRFbGVtZW50BAAAAAckbWF0Y2gwBQAAAAJ0eAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAPRGF0YVRyYW5zYWN0aW9uBAAAAAJkMQUAAAAHJG1hdGNoMAkBAAAAAiE9AAAAAgkAAZEAAAACCAUAAAACZDEAAAAEZGF0YQAAAAAAAAAAAAkBAAAACURhdGFFbnRyeQAAAAICAAAAAmhhBgMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAATVHJhbnNmZXJUcmFuc2FjdGlvbgYHBAAAABhuZU9wdGlvbkFuZEV4dHJhY3RIZWlnaHQEAAAAByRtYXRjaDAFAAAAAnR4AwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAAA9EYXRhVHJhbnNhY3Rpb24GAwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAABNUcmFuc2ZlclRyYW5zYWN0aW9uCQEAAAACIT0AAAACCQEAAAAHZXh0cmFjdAAAAAEJAAPpAAAAAQgFAAAAAnR4AAAAAmlkAAAAAAAAAAAABwQAAAACbmUDAwUAAAAGbmVQcmltBQAAABhuZURhdGFFbnRyeUFuZEdldEVsZW1lbnQHBQAAABhuZU9wdGlvbkFuZEV4dHJhY3RIZWlnaHQHBAAAAAdndGVMb25nAwkAAGYAAAACAAAAAAAAAAPoAAAAAAAAAAPnCQAAZwAAAAIAAAAAAAAAA+gAAAAAAAAAA+cHBAAAAAtnZXRMaXN0U2l6ZQQAAAAHJG1hdGNoMAUAAAACdHgDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAD0RhdGFUcmFuc2FjdGlvbgQAAAACZDIFAAAAByRtYXRjaDAJAQAAAAIhPQAAAAIJAAGQAAAAAQgFAAAAAmQyAAAABGRhdGEAAAAAAAAAAAADCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAE1RyYW5zZmVyVHJhbnNhY3Rpb24GBwQAAAAFdW5hcnkDCQAAAAAAAAIA//////////8A//////////8JAAAAAAAAAgcJAQAAAAEhAAAAAQYHBAAAAAhmckFjdGlvbgkAAAAAAAACCQAAawAAAAMAAAAAAAAAAAwAAAAAAAAAAAMAAAAAAAAAAAQAAAAAAAAAAAkEAAAACGJ5dGVzT3BzBAAAAAckbWF0Y2gwBQAAAAJ0eAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAPRGF0YVRyYW5zYWN0aW9uBAAAAAJkMwUAAAAHJG1hdGNoMAMDAwMJAQAAAAIhPQAAAAIJAADIAAAAAQgFAAAAAmQzAAAACWJvZHlCeXRlcwAAAAAAAAAAAAkBAAAAAiE9AAAAAgkAAMkAAAACCAUAAAACZDMAAAAJYm9keUJ5dGVzAAAAAAAAAAABAQAAAAIJMQcJAQAAAAIhPQAAAAIJAADKAAAAAggFAAAAAmQzAAAACWJvZHlCeXRlcwAAAAAAAAAAAQEAAAACCTEHCQEAAAACIT0AAAACCQEAAAAOdGFrZVJpZ2h0Qnl0ZXMAAAACCAUAAAACZDMAAAAJYm9keUJ5dGVzAAAAAAAAAAABAQAAAAIJMQcJAQAAAAIhPQAAAAIJAQAAAA5kcm9wUmlnaHRCeXRlcwAAAAIIBQAAAAJkMwAAAAlib2R5Qnl0ZXMAAAAAAAAAAAEBAAAAAgkxBwMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAATVHJhbnNmZXJUcmFuc2FjdGlvbgQAAAACdDEFAAAAByRtYXRjaDAJAAAAAAAAAgkBAAAACWlzRGVmaW5lZAAAAAEIBQAAAAJ0MQAAAApmZWVBc3NldElkBwcEAAAABnN0ck9wcwMDAwMJAQAAAAIhPQAAAAIJAAExAAAAAQIAAAAEaGFoYQAAAAAAAAAAAAkBAAAAAiE9AAAAAgkAAS8AAAACAgAAAARoYWhhAAAAAAAAAAABAgAAAAAHCQEAAAACIT0AAAACCQABMAAAAAICAAAABGhhaGEAAAAAAAAAAAACAAAAAAcJAQAAAAIhPQAAAAIJAQAAAAl0YWtlUmlnaHQAAAACAgAAAARoYWhhAAAAAAAAAAABAgAAAAAHCQEAAAACIT0AAAACCQEAAAAJZHJvcFJpZ2h0AAAAAgIAAAAEaGFoYQAAAAAAAAAAAAIAAAAABwQAAAAEcHVyZQMDAwMDAwMFAAAABWJhc2ljBQAAAAJuZQcFAAAAB2d0ZUxvbmcHBQAAAAtnZXRMaXN0U2l6ZQcFAAAABXVuYXJ5BwUAAAAIZnJBY3Rpb24HBQAAAAhieXRlc09wcwcFAAAABnN0ck9wcwcEAAAABnR4QnlJZAQAAAAHJG1hdGNoMAUAAAACdHgDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAD0RhdGFUcmFuc2FjdGlvbgYDCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAE1RyYW5zZmVyVHJhbnNhY3Rpb24EAAAAAWcJAQAAAAdleHRyYWN0AAAAAQkAA+gAAAABAQAAACCB+LHr7irQ7N6spxF91VJYssai++EyJEw1WFYwXb0UCwkAAAAAAAACCAUAAAABZwAAAAJpZAEAAAAggfix6+4q0OzerKcRfdVSWLLGovvhMiRMNVhWMF29FAsHBAAAAAdlbnRyaWVzBAAAAAckbWF0Y2gwBQAAAAJ0eAMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAPRGF0YVRyYW5zYWN0aW9uBAAAAAFkBQAAAAckbWF0Y2gwBAAAAANpbnQJAQAAAAdleHRyYWN0AAAAAQkABBAAAAACCAUAAAABZAAAAARkYXRhAgAAAANpbnQEAAAABGJvb2wJAQAAAAdleHRyYWN0AAAAAQkABBEAAAACCAUAAAABZAAAAARkYXRhAgAAAARib29sBAAAAARibG9iCQEAAAAHZXh0cmFjdAAAAAEJAAQSAAAAAggFAAAAAWQAAAAEZGF0YQIAAAAEYmxvYgQAAAADc3RyCQEAAAAHZXh0cmFjdAAAAAEJAAQTAAAAAggFAAAAAWQAAAAEZGF0YQIAAAADc3RyBAAAAAlkYXRhQnlLZXkDAwMJAAAAAAAAAgkAAaQAAAABBQAAAANpbnQCAAAAAjI0BgkAAAAAAAACCQABpQAAAAEFAAAABGJvb2wCAAAABHRydWUGCQAAZgAAAAIJAADIAAAAAQUAAAAEYmxvYgAAAAAAAAAAAAYJAAAAAAAAAgUAAAADc3RyAgAAAAR0ZXN0BAAAAAJkMAkBAAAAB2V4dHJhY3QAAAABCQEAAAAKZ2V0SW50ZWdlcgAAAAIIBQAAAAFkAAAABGRhdGEAAAAAAAAAAAAEAAAAAmQxCQEAAAAHZXh0cmFjdAAAAAEJAQAAAApnZXRCb29sZWFuAAAAAggFAAAAAWQAAAAEZGF0YQAAAAAAAAAAAQQAAAACZDIJAQAAAAlnZXRCaW5hcnkAAAACCAUAAAABZAAAAARkYXRhAAAAAAAAAAACBAAAAAJkMwkBAAAACWdldFN0cmluZwAAAAIIBQAAAAFkAAAABGRhdGEAAAAAAAAAAAMEAAAAC2RhdGFCeUluZGV4AwMDCQAAAAAAAAIJAAGaAAAAAQUAAAACZDABAAAABGm3HXkGCQAAAAAAAAIJAAGcAAAAAQUAAAACZDEBAAAABIIYo5IGCQEAAAAJaXNEZWZpbmVkAAAAAQUAAAACZDIGCQAAAAAAAAIJAAGbAAAAAQkBAAAAB2V4dHJhY3QAAAABBQAAAAJkMwEAAAAEmnopqgMFAAAACWRhdGFCeUtleQUAAAALZGF0YUJ5SW5kZXgHAwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAABNUcmFuc2ZlclRyYW5zYWN0aW9uBAAAAANhZGQJAQAAAAdBZGRyZXNzAAAAAQEAAAAaAVRkF43fgOyfPt/WVujqaJKrKOnneu66nJ0EAAAABGxvbmcJAAAAAAAAAgkBAAAAB2V4dHJhY3QAAAABCQAEGgAAAAIFAAAAA2FkZAIAAAADaW50AAAAAAAAAAAYBAAAAAVib29sMQkAAAAAAAACCQEAAAAHZXh0cmFjdAAAAAEJAAQbAAAAAgUAAAADYWRkAgAAAARib29sBgQAAAADYmluCQAAAAAAAAIJAQAAAAdleHRyYWN0AAAAAQkABBwAAAACBQAAAANhZGQCAAAABGJsb2IBAAAABWFsaWNlBAAAAARzdHIxCQAAAAAAAAIJAQAAAAdleHRyYWN0AAAAAQkABB0AAAACBQAAAANhZGQCAAAAA3N0cgIAAAAEdGVzdAMDAwUAAAAEbG9uZwUAAAAFYm9vbDEHBQAAAANiaW4HBQAAAARzdHIxBwMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAWQ3JlYXRlQWxpYXNUcmFuc2FjdGlvbgkAAAIAAAABAgAAAAVvaCBubwMJAAABAAAAAgUAAAAHJG1hdGNoMAIAAAAPQnVyblRyYW5zYWN0aW9uCQEAAAAFdGhyb3cAAAAABwQAAAAHYUZyb21QSwkAAAAAAAACCQEAAAAUYWRkcmVzc0Zyb21QdWJsaWNLZXkAAAABCAUAAAACdHgAAAAPc2VuZGVyUHVibGljS2V5CAUAAAACdHgAAAAGc2VuZGVyBAAAAA9hRnJvbVN0ck9yUmVjaXAEAAAAByRtYXRjaDAFAAAAAnR4AwkAAAEAAAACBQAAAAckbWF0Y2gwAgAAAA9EYXRhVHJhbnNhY3Rpb24JAAAAAAAAAgkBAAAAEWFkZHJlc3NGcm9tU3RyaW5nAAAAAQIAAAAjM041R1JxekRCaGpWWG5DbjQ0YmFIY3oyR29aeTVxTHh0VGgJAQAAAAdBZGRyZXNzAAAAAQEAAAAaAVSoYvWtn5xSOgZyscKLnKFwwV9ogl+Q7gADCQAAAQAAAAIFAAAAByRtYXRjaDACAAAAE1RyYW5zZmVyVHJhbnNhY3Rpb24EAAAAAnQxBQAAAAckbWF0Y2gwCQAAAAAAAAIJAAQkAAAAAQgFAAAAAnQxAAAACXJlY2lwaWVudAkBAAAAB0FkZHJlc3MAAAABAQAAABoBVGQXjd+A7J8+39ZW6Opokqso6ed67rqcnQcEAAAACGJhbGFuY2VzAwkAAGYAAAACCQAD6wAAAAIIBQAAAAJ0eAAAAAZzZW5kZXIFAAAABHVuaXQAAAAAAAAAAAAJAQAAAAIhPQAAAAIJAQAAAAx3YXZlc0JhbGFuY2UAAAABCAUAAAACdHgAAAAGc2VuZGVyAAAAAAAAAAAABwQAAAAFd2F2ZXMDAwMDAwUAAAAGdHhCeUlkBQAAAAdlbnRyaWVzBwUAAAAIYmFsYW5jZXMHBQAAAAdhRnJvbVBLBwUAAAAPYUZyb21TdHJPclJlY2lwBwkAAGYAAAACBQAAAAZoZWlnaHQAAAAAAAAAAAAHBAAAAANia3MDAwkBAAAAAiE9AAAAAgkAAfYAAAABAQAAAAABAAAAAAkBAAAAAiE9AAAAAgkAAfUAAAABAQAAAAABAAAAAAcJAQAAAAIhPQAAAAIJAAH3AAAAAQEAAAAAAQAAAAAHBAAAAANzaWcJAQAAAAIhPQAAAAIJAAH0AAAAAwEAAAACGr4BAAAAAgA8AQAAAAI1uAYEAAAABXN0cjU4CQAAAAAAAAIJAAJZAAAAAQkAAlgAAAABCAUAAAACdHgAAAACaWQIBQAAAAJ0eAAAAAJpZAQAAAAFc3RyNjQJAAAAAAAAAgkAAlsAAAABCQACWgAAAAEIBQAAAAJ0eAAAAAJpZAgFAAAAAnR4AAAAAmlkBAAAAAZjcnlwdG8DAwMFAAAAA2JrcwUAAAADc2lnBwUAAAAFc3RyNTgHBQAAAAVzdHI2NAcDBQAAAANybmQDBQAAAARwdXJlBQAAAAV3YXZlcwcFAAAABmNyeXB0bw==") SerdeV1.serialize(compiledScript) shouldBe bytes } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala index f307953d23e..c9eeaf2bc27 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/TransactionBindingsTest.scala @@ -881,7 +881,7 @@ class TransactionBindingsTest extends PropSpec with PathMockFactory with EitherV ByteStr.empty ) for { - compileResult <- compiler.ExpressionCompiler(ctx.compilerContext, expr) + compileResult <- compiler.ExpressionCompiler(ctx.compilerContext, V3, expr) (typedExpr, _) = compileResult r <- EvaluatorV1().apply[EVALUATED](ctx.evaluationContext(environment), typedExpr).leftMap(_.message) } yield r @@ -913,7 +913,7 @@ class TransactionBindingsTest extends PropSpec with PathMockFactory with EitherV ) for { - compileResult <- ExpressionCompiler(ctx.compilerContext, expr) + compileResult <- ExpressionCompiler(ctx.compilerContext, V2, expr) (typedExpr, _) = compileResult r <- EvaluatorV1().apply[EVALUATED](ctx.evaluationContext(env), typedExpr).leftMap(_.message) } yield r diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala index 43b9d510d85..89c5bae26d0 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/package.scala @@ -27,7 +27,7 @@ package object predef { def runScript[T <: EVALUATED](script: String, version: StdLibVersion, t: In, blockchain: Blockchain, chainId: Byte): Either[String, T] = { val expr = Parser.parseExpr(script).get.value for { - compileResult <- ExpressionCompiler(compilerContext(version, Expression, isAssetScript = false), expr) + compileResult <- ExpressionCompiler(compilerContext(version, Expression, isAssetScript = false), version, expr) (typedExpr, _) = compileResult directives = DirectiveSet(version, Account, Expression).explicitGet() evalContext <- BlockchainContext.build( diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala index ed46169e843..2523329d952 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/BalancesV4Test.scala @@ -1,11 +1,11 @@ package com.wavesplatform.state.diffs.smart.scenarios import cats.syntax.semigroup.* +import com.wavesplatform.api.common.CommonAccountsApi import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.db.WithState import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.history.SnapshotOps import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.Global import com.wavesplatform.lang.directives.DirectiveSet @@ -95,13 +95,12 @@ class BalancesV4Test extends PropSpec with WithState { Seq(TestBlock.create(b)), TestBlock.create(Seq(ci)), rideV4Activated - ) { case (d, s) => + ) { case (snapshot, blockchain) => val apiBalance = - com.wavesplatform.api.common - .CommonAccountsApi(() => SnapshotBlockchain(s, SnapshotOps.fromDiff(d, s).explicitGet()), rdb, s) + CommonAccountsApi(() => SnapshotBlockchain(blockchain, snapshot), rdb, blockchain) .balanceDetails(acc1.toAddress) .explicitGet() - val data = d.accountData(dapp.toAddress) + val data = snapshot.accountData(dapp.toAddress) data("available") shouldBe IntegerDataEntry("available", apiBalance.available) apiBalance.available shouldBe 16 * Constants.UnitsInWave data("regular") shouldBe IntegerDataEntry("regular", apiBalance.regular) @@ -133,7 +132,7 @@ class BalancesV4Test extends PropSpec with WithState { | assetBalance(Address(base58'$acc'), this.id) == $a && assetBalance(Alias("alias"), this.id) == $a """.stripMargin val parsedScript = Parser.parseExpr(script).get.value - ExprScript(V4, ExpressionCompiler(ctx.compilerContext, parsedScript).explicitGet()._1) + ExprScript(V4, ExpressionCompiler(ctx.compilerContext, V4, parsedScript).explicitGet()._1) .explicitGet() } @@ -195,7 +194,7 @@ class BalancesV4Test extends PropSpec with WithState { | wavesBalance(Address(base58'$acc')).regular == $w """.stripMargin val parsedScript = Parser.parseExpr(script).get.value - ExprScript(V4, ExpressionCompiler(ctx.compilerContext, parsedScript).explicitGet()._1) + ExprScript(V4, ExpressionCompiler(ctx.compilerContext, V4, parsedScript).explicitGet()._1) .explicitGet() } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/MultiSig2of3Test.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/MultiSig2of3Test.scala index 94e729bea6a..dd3ccb8635a 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/MultiSig2of3Test.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/MultiSig2of3Test.scala @@ -37,7 +37,7 @@ class MultiSig2of3Test extends PropSpec with WithState { | """.stripMargin val untyped = Parser.parseExpr(script).get.value - ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untyped).explicitGet()._1 + ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untyped).explicitGet()._1 } val preconditionsAndTransfer: (GenesisTransaction, SetScriptTransaction, TransferTransaction, Seq[ByteStr]) = { diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala index aa4eae87dbb..05e1d718a45 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/NotaryControlledTransferScenarioTest.scala @@ -58,7 +58,7 @@ class NotaryControlledTransferScenarioTest extends PropSpec with WithState { | } """.stripMargin val untypedScript = Parser.parseExpr(assetScript).get.value - val typedScript = ExprScript(ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedScript).explicitGet()._1) + val typedScript = ExprScript(ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untypedScript).explicitGet()._1) .explicitGet() val issue = TxHelpers.issue(company, 100, script = Some(typedScript)) @@ -88,7 +88,7 @@ class NotaryControlledTransferScenarioTest extends PropSpec with WithState { private def eval(code: String) = { val untyped = Parser.parseExpr(code).get.value - val typed = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untyped).map(_._1) + val typed = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untyped).map(_._1) typed.flatMap(r => EvaluatorV1().apply[EVALUATED](dummyEvalContext, r).leftMap(_.message)) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OneProofForNonScriptedAccountTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OneProofForNonScriptedAccountTest.scala index e917798d072..921b02d846f 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OneProofForNonScriptedAccountTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OneProofForNonScriptedAccountTest.scala @@ -27,7 +27,7 @@ class OneProofForNonScriptedAccountTest extends PropSpec with WithState { val (genesis, _, transfer) = s val transferWithExtraProof = transfer.copy(proofs = Proofs(Seq(ByteStr.empty, ByteStr(Array(1: Byte))))) assertDiffEi(Seq(TestBlock.create(Seq(genesis))), TestBlock.create(Seq(transferWithExtraProof)), smartEnabledFS)( - totalDiffEi => totalDiffEi should produce("must have exactly 1 proof") + snapshotEi => snapshotEi should produce("must have exactly 1 proof") ) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OnlyTransferIsAllowedTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OnlyTransferIsAllowedTest.scala index 6959cea1241..af9942b3193 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OnlyTransferIsAllowedTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OnlyTransferIsAllowedTest.scala @@ -32,12 +32,12 @@ class OnlyTransferIsAllowedTest extends PropSpec with WithState { | } """.stripMargin val untyped = Parser.parseExpr(scriptText).get.value - val transferAllowed = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untyped).explicitGet()._1 + val transferAllowed = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untyped).explicitGet()._1 val (genesis, script, lease, transfer) = preconditions(transferAllowed) assertDiffAndState(Seq(TestBlock.create(Seq(genesis, script))), TestBlock.create(Seq(transfer)), smartEnabledFS) { case _ => () } assertDiffEi(Seq(TestBlock.create(Seq(genesis, script))), TestBlock.create(Seq(lease)), smartEnabledFS)( - totalDiffEi => totalDiffEi should produce("TransactionNotAllowedByScript") + snapshotEi => snapshotEi should produce("TransactionNotAllowedByScript") ) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OracleDataTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OracleDataTest.scala index c23560e0447..b6f6a8d77c5 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OracleDataTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/OracleDataTest.scala @@ -55,7 +55,7 @@ class OracleDataTest extends PropSpec with WithState { |}""".stripMargin val untypedAllFieldsRequiredScript = Parser.parseExpr(allFieldsRequiredScript).get.value val typedAllFieldsRequiredScript = - ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedAllFieldsRequiredScript).explicitGet()._1 + ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untypedAllFieldsRequiredScript).explicitGet()._1 val setScript = TxHelpers.setScript(master, ExprScript(typedAllFieldsRequiredScript).explicitGet()) val transferFromScripted = TxHelpers.transfer(master, alice.toAddress) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransactionFieldAccessTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransactionFieldAccessTest.scala index fa52ffda1e8..722fd8fca31 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransactionFieldAccessTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransactionFieldAccessTest.scala @@ -24,7 +24,7 @@ class TransactionFieldAccessTest extends PropSpec with WithState { val genesis = TxHelpers.genesis(master.toAddress) val untyped = Parser.parseExpr(code).get.value - val typed = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untyped).explicitGet()._1 + val typed = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), V1, untyped).explicitGet()._1 val setScript = TxHelpers.setScript(master, ExprScript(typed).explicitGet()) val transfer = TxHelpers.transfer(master, recipient.toAddress, ENOUGH_AMT / 2) val lease = TxHelpers.lease(master, recipient.toAddress, ENOUGH_AMT / 2) @@ -47,7 +47,7 @@ class TransactionFieldAccessTest extends PropSpec with WithState { val (genesis, setScript, lease, transfer) = preconditionsTransferAndLease(script) assertDiffAndState(Seq(TestBlock.create(Seq(genesis, setScript))), TestBlock.create(Seq(transfer)), smartEnabledFS) { case _ => () } assertDiffEi(Seq(TestBlock.create(Seq(genesis, setScript))), TestBlock.create(Seq(lease)), smartEnabledFS)( - totalDiffEi => totalDiffEi should produce("TransactionNotAllowedByScript") + snapshotEi => snapshotEi should produce("TransactionNotAllowedByScript") ) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransferByIdTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransferByIdTest.scala index eb3ba6de556..925ea4e4b6d 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransferByIdTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/scenarios/TransferByIdTest.scala @@ -34,7 +34,7 @@ class TransferByIdTest extends PropSpec with WithState { val expr: EXPR = { val parsed = Parser.parseExpr(scriptSrc).get.value - ExpressionCompiler(compilerContext(V3, Expression, isAssetScript = false), parsed).explicitGet()._1 + ExpressionCompiler(compilerContext(V3, Expression, isAssetScript = false), V3, parsed).explicitGet()._1 } property("Transfer by id works fine") { diff --git a/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala b/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala index 9ed626dccda..eb25b4b1ebc 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala @@ -1,6 +1,5 @@ package com.wavesplatform.state.snapshot -import cats.data.Ior import com.google.common.primitives.{Ints, Longs, UnsignedBytes} import com.wavesplatform.account.{AddressScheme, Alias} import com.wavesplatform.common.state.ByteStr @@ -8,14 +7,13 @@ import com.wavesplatform.common.utils.* import com.wavesplatform.crypto.DigestLength import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance -import com.wavesplatform.history.SnapshotOps import com.wavesplatform.lang.v1.estimator.ScriptEstimatorV1 import com.wavesplatform.state.* import com.wavesplatform.state.TxMeta.Status.* import com.wavesplatform.state.TxStateSnapshotHashBuilder.{KeyType, TxStatusInfo} import com.wavesplatform.state.reader.LeaseDetails import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxHelpers.invoke import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.transaction.{AssetIdLength, TxHelpers} @@ -37,8 +35,8 @@ class TxStateSnapshotHashSpec extends PropSpec with WithDomain { private val leaseId1 = ByteStr.fill(DigestLength)(6) private val leaseId2 = ByteStr.fill(DigestLength)(7) - private val leaseDetails1 = LeaseDetails(TxHelpers.signer(1).publicKey, address2, 1.waves, LeaseDetails.Status.Active, leaseId1, 2) - private val leaseDetails2 = LeaseDetails(TxHelpers.signer(1).publicKey, address2, 1.waves, LeaseDetails.Status.Cancelled(1, None), leaseId2, 2) + private val leaseDetails1 = LeaseSnapshot(TxHelpers.signer(1).publicKey, address2, 1.waves, LeaseDetails.Status.Active) + private val leaseDetails2 = LeaseSnapshot(TxHelpers.signer(1).publicKey, address2, 1.waves, LeaseDetails.Status.Cancelled(1, None)) private val addr1Balance = 10.waves private val addr2Balance = 20.waves @@ -92,29 +90,46 @@ class TxStateSnapshotHashSpec extends PropSpec with WithDomain { private val dataEntry = StringDataEntry("key", "value") - private val diff = Diff( - portfolios = Map(address1 -> addr1PortfolioDiff, address2 -> addr2PortfolioDiff), - issuedAssets = VectorMap(assetId1 -> assetInfo1, assetId2 -> assetInfo2, assetId3 -> assetInfo3, assetId4 -> assetInfo4), - updatedAssets = Map( - assetId1 -> Ior.Both(updatedAssetInfo1, updatedAssetVolumeInfo1), - assetId2 -> Ior.Left(updatedAssetInfo2), - assetId3 -> Ior.Right(updatedAssetVolumeInfo3) + private val snapshot = StateSnapshot( + balances = VectorMap( + (address1, Waves) -> (addr1PortfolioDiff.balance + addr1Balance), + (address2, assetId1) -> addr2PortfolioDiff.assets.head._2 + ), + leaseBalances = Map(address1 -> addr1PortfolioDiff.lease), + assetStatics = StateSnapshot.assetStatics( + VectorMap( + assetId1 -> assetInfo1, + assetId2 -> assetInfo2, + assetId3 -> assetInfo3, + assetId4 -> assetInfo4 + ) + ), + assetVolumes = VectorMap( + assetId1 -> updatedAssetVolumeInfo1, + assetId2 -> assetInfo2.volume, + assetId3 -> updatedAssetVolumeInfo3, + assetId4 -> assetInfo4.volume + ), + assetNamesAndDescriptions = VectorMap( + assetId1 -> updatedAssetInfo1, + assetId2 -> updatedAssetInfo2, + assetId3 -> assetInfo3.dynamic, + assetId4 -> assetInfo4.dynamic ), aliases = Map(addr1Alias1 -> address1, addr2Alias -> address2, addr1Alias2 -> address1), orderFills = Map(orderId -> volumeAndFee), - leaseState = Map(leaseId1 -> leaseDetails1, leaseId2 -> leaseDetails2), - scripts = Map(TxHelpers.signer(2).publicKey -> Some(accountScriptInfo)), - assetScripts = Map(assetId1 -> Some(assetScriptInfo)), + leaseStates = Map(leaseId1 -> leaseDetails1, leaseId2 -> leaseDetails2), + accountScripts = Map(TxHelpers.signer(2).publicKey -> Some(accountScriptInfo)), + assetScripts = Map(assetId1 -> assetScriptInfo), accountData = Map(address1 -> Map(dataEntry.key -> dataEntry)), - sponsorship = Map(assetId1 -> sponsorship) + sponsorships = Map(assetId1 -> sponsorship) ) def hash(bs: Seq[Array[Byte]]): ByteStr = ByteStr(com.wavesplatform.crypto.fastHash(bs.reduce(_ ++ _))) property("correctly create transaction state snapshot hash from snapshot") { - withDomain(DomainPresets.RideV6, balances = Seq(AddrWithBalance(address1, addr1Balance), AddrWithBalance(address2, addr2Balance))) { d => - val snapshot = SnapshotOps.fromDiff(diff, d.blockchain).explicitGet() - val tx = invoke() + withDomain(DomainPresets.RideV6, balances = Seq(AddrWithBalance(address1, addr1Balance), AddrWithBalance(address2, addr2Balance))) { _ => + val tx = invoke() testHash(snapshot, Some(TxStatusInfo(tx.id(), Succeeded)), Array()) testHash(snapshot, Some(TxStatusInfo(tx.id(), Failed)), tx.id().arr :+ 1) testHash(snapshot, Some(TxStatusInfo(tx.id(), Elided)), tx.id().arr :+ 2) diff --git a/node/src/test/scala/com/wavesplatform/state/utils/package.scala b/node/src/test/scala/com/wavesplatform/state/utils/package.scala index 12cecbbd777..dcb7a10e423 100644 --- a/node/src/test/scala/com/wavesplatform/state/utils/package.scala +++ b/node/src/test/scala/com/wavesplatform/state/utils/package.scala @@ -15,13 +15,13 @@ package object utils { def addressTransactions( rdb: RDB, - diff: => Option[(Height, StateSnapshot)], + snapshot: => Option[(Height, StateSnapshot)], address: Address, types: Set[Transaction.Type], fromId: Option[ByteStr] )(implicit s: Scheduler): Seq[(Height, Transaction)] = AddressTransactions - .allAddressTransactions(rdb, diff, address, None, types, fromId) + .allAddressTransactions(rdb, snapshot, address, None, types, fromId) .map { case (tm, tx, _) => tm.height -> tx } .toListL .runSyncUnsafe() diff --git a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala index e1aebcc9597..2c91c553678 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala @@ -36,12 +36,12 @@ object TxHelpers { def signer(i: Int): SeedKeyPair = KeyPair(Ints.toByteArray(i)) def address(i: Int): Address = signer(i).toAddress - def defaultSigner: SeedKeyPair = signer(0) - def defaultAddress: Address = defaultSigner.toAddress - def secondSigner: SeedKeyPair = signer(1) - def secondAddress: Address = secondSigner.toAddress + val defaultSigner: SeedKeyPair = signer(0) + val defaultAddress: Address = defaultSigner.toAddress + val secondSigner: SeedKeyPair = signer(1) + val secondAddress: Address = secondSigner.toAddress - def defaultEthSigner: ECKeyPair = defaultSigner.toEthKeyPair + val defaultEthSigner: ECKeyPair = defaultSigner.toEthKeyPair def accountSeqGenerator(numberAccounts: Int, amount: Long): Seq[ParsedTransfer] = { val firstAccountNum = 100 diff --git a/node/src/test/scala/com/wavesplatform/transaction/assets/exchange/EthOrderSpec.scala b/node/src/test/scala/com/wavesplatform/transaction/assets/exchange/EthOrderSpec.scala index 16080b9d343..d5884cfe62a 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/assets/exchange/EthOrderSpec.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/assets/exchange/EthOrderSpec.scala @@ -6,7 +6,6 @@ import com.wavesplatform.test.{FlatSpec, TestTime} import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.BlockchainStubHelpers import com.wavesplatform.common.utils.* -import com.wavesplatform.history.SnapshotOps.TransactionStateSnapshotExt import com.wavesplatform.state.diffs.TransactionDiffer import com.wavesplatform.transaction.{TxExchangeAmount, TxHelpers, TxMatcherFee, TxOrderPrice, TxVersion} import com.wavesplatform.utils.{DiffMatchers, EthEncoding, EthHelpers, JsonMatchers} @@ -142,8 +141,8 @@ class EthOrderSpec val differ = blockchain.stub.transactionDiffer(TestTime(100)) _ val transaction = TxHelpers.exchange(ethBuyOrder, ethSellOrder, price = 100, version = TxVersion.V3, timestamp = 100) - val diff = differ(transaction).resultE.explicitGet() - diff should containAppliedTx(transaction.id()) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) } it should "work in exchange transaction with an old order" in { @@ -175,8 +174,8 @@ class EthOrderSpec val differ = TransactionDiffer(Some(1L), 100L)(blockchain, _) val transaction = TxHelpers.exchange(buyOrder, ethSellOrder, price = 100, version = TxVersion.V3, timestamp = 100) - val diff = differ(transaction).resultE.explicitGet().toDiff(blockchain) - diff should containAppliedTx(transaction.id()) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) } it should "recover valid ids of exchange tx" in { @@ -354,8 +353,8 @@ class EthOrderSpec val differ = TransactionDiffer(Some(1L), 100L)(blockchain, _) val transaction = TxHelpers.exchange(buyOrder, ethSellOrder, price = 100, version = TxVersion.V3, timestamp = 100) - val diff = differ(transaction).resultE.explicitGet().toDiff(blockchain) - diff should containAppliedTx(transaction.id()) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) } it should "work in exchange transaction with matcher script" in { @@ -393,8 +392,8 @@ class EthOrderSpec val differ = blockchain.stub.transactionDiffer(TestTime(100)) _ val transaction = TxHelpers.exchange(ethBuyOrder, ethSellOrder, price = 100, version = TxVersion.V3, timestamp = 100) - val diff = differ(transaction).resultE.explicitGet() - diff should containAppliedTx(transaction.id()) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) } it should "be serialized correctly to EIP-712 json with and without attachment (NODE-996)" in { diff --git a/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionSpec.scala b/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionSpec.scala index 339036b83a2..c3d1e65655e 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionSpec.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/smart/EthereumTransactionSpec.scala @@ -4,7 +4,6 @@ import com.wavesplatform.account.AddressScheme import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.* import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.state.Portfolio import com.wavesplatform.state.diffs.produceRejectOrFailedDiff import com.wavesplatform.test.{FlatSpec, TestTime, produce} import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} @@ -106,9 +105,14 @@ class EthereumTransactionSpec val transfer = EthTxGenerator.generateEthTransfer(senderAccount, recipientAddress, LongMaxMinusFee, Waves) val assetTransfer = EthTxGenerator.generateEthTransfer(senderAccount, recipientAddress, Long.MaxValue, TestAsset) - differ(transfer).combineF(differ(assetTransfer)).explicitGet().portfolios shouldBe Map( - senderAddress -> Portfolio.build(-Long.MaxValue, TestAsset, -Long.MaxValue), - recipientAddress -> Portfolio.build(LongMaxMinusFee, TestAsset, Long.MaxValue) + differ(assetTransfer).balances shouldBe Map( + (senderAddress, TestAsset) -> 0, + (senderAddress, Waves) -> (LongMaxMinusFee + transfer.fee.longValue()), + (recipientAddress, TestAsset) -> Long.MaxValue + ) + differ(transfer).balances shouldBe Map( + (senderAddress, Waves) -> transfer.fee.longValue(), + (recipientAddress, Waves) -> LongMaxMinusFee ) } @@ -236,23 +240,23 @@ class EthereumTransactionSpec ), Seq(Payment(321, IssuedAsset(ByteStr(EthStubBytes32)))) ) - val diff = differ(transaction).resultE.explicitGet() - diff should containAppliedTx(transaction.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson("""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } it should "not work with union type" in { @@ -291,8 +295,8 @@ class EthereumTransactionSpec Seq(Payment(321, IssuedAsset(ByteStr(EthStubBytes32)))) ) - val diff = differ(transaction).resultE - diff should produce("Function not defined: 1f9773e9") + val snapshot = differ(transaction).resultE + snapshot should produce("Function not defined: 1f9773e9") } it should "work with no arguments" in { @@ -332,23 +336,23 @@ class EthereumTransactionSpec Seq(), Seq(Payment(321, IssuedAsset(ByteStr(EthStubBytes32)))) ) - val diff = differ(transaction).resultE.explicitGet() - diff should containAppliedTx(transaction.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson("""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } it should "work with no payments" in { @@ -382,23 +386,23 @@ class EthereumTransactionSpec val differ = blockchain.stub.transactionDiffer(TestTime(System.currentTimeMillis())) val transaction = EthTxGenerator.generateEthInvoke(invokerAccount, dAppAccount.toAddress, "deposit", Seq(), Seq()) - val diff = differ(transaction).resultE.explicitGet() - diff should containAppliedTx(transaction.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson("""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } it should "fail with max+1 payments" in { @@ -478,23 +482,23 @@ class EthereumTransactionSpec Seq(), Seq(Payment(321, IssuedAsset(ByteStr(EthStubBytes32)))) ) - val diff = differ(transaction).resultE.explicitGet() - diff should containAppliedTx(transaction.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson("""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | } ], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson("""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | } ], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } it should "return money in transfers asset+waves" in { @@ -535,28 +539,28 @@ class EthereumTransactionSpec Seq(), Nil ) - val diff = differ(transaction).resultE.explicitGet() - diff should containAppliedTx(transaction.id()) - Json.toJson(diff.scriptResults.values.head) should matchJson(s"""{ - | "data" : [ ], - | "transfers" : [ { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : null, - | "amount" : 123 - | }, - | { - | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "asset" : "$TestAsset", - | "amount" : 123 - | }], - | "issues" : [ ], - | "reissues" : [ ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - |}""".stripMargin) + val snapshot = differ(transaction).resultE.explicitGet() + snapshot should containAppliedTx(transaction.id()) + Json.toJson(snapshot.scriptResults.values.head) should matchJson(s"""{ + | "data" : [ ], + | "transfers" : [ { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : null, + | "amount" : 123 + | }, + | { + | "address" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", + | "asset" : "$TestAsset", + | "amount" : 123 + | }], + | "issues" : [ ], + | "reissues" : [ ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + |}""".stripMargin) } it should "test minimum fee" in { diff --git a/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala b/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala index 3a4d9ab730a..e8e1a4ca84e 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/smart/script/estimator/FunctionComplexityTest.scala @@ -106,7 +106,7 @@ class FunctionComplexityTest(estimator: ScriptEstimator) extends PropSpec { property("estimate script with all functions") { def check(version: StdLibVersion, expectedCost: Int) = { - val expr = ExpressionCompiler(ctx(version).compilerContext, getAllFuncExpression(version)).explicitGet()._1 + val expr = ExpressionCompiler(ctx(version).compilerContext, version, getAllFuncExpression(version)).explicitGet()._1 estimate(expr, ctx(version), utils.functionCosts(version)) shouldBe Right(expectedCost) } diff --git a/node/src/test/scala/com/wavesplatform/utils/DiffMatchers.scala b/node/src/test/scala/com/wavesplatform/utils/DiffMatchers.scala index 45b07ec9eba..27eded1744f 100644 --- a/node/src/test/scala/com/wavesplatform/utils/DiffMatchers.scala +++ b/node/src/test/scala/com/wavesplatform/utils/DiffMatchers.scala @@ -1,25 +1,24 @@ package com.wavesplatform.utils import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.state.Diff -import com.wavesplatform.state.TxMeta.Status +import com.wavesplatform.state.StateSnapshot +import com.wavesplatform.state.TxMeta.Status.Succeeded import org.scalatest.matchers.{MatchResult, Matcher} trait DiffMatchers { def containAppliedTx(transactionId: ByteStr) = new DiffAppliedTxMatcher(transactionId, true) def containFailedTx(transactionId: ByteStr) = new DiffAppliedTxMatcher(transactionId, false) - class DiffAppliedTxMatcher(transactionId: ByteStr, shouldBeApplied: Boolean) extends Matcher[Diff] { - override def apply(diff: Diff): MatchResult = { - val isApplied = diff.transaction(transactionId) match { - case Some(nt) if nt.status == Status.Succeeded => true - case _ => false + class DiffAppliedTxMatcher(transactionId: ByteStr, shouldBeApplied: Boolean) extends Matcher[StateSnapshot] { + override def apply(snapshot: StateSnapshot): MatchResult = { + val isApplied = snapshot.transactions.get(transactionId) match { + case Some(nt) if nt.status == Succeeded => true + case _ => false } - MatchResult( shouldBeApplied == isApplied, - s"$transactionId was not ${if (shouldBeApplied) "applied" else "failed"}: $diff", - s"$transactionId was ${if (shouldBeApplied) "applied" else "failed"}: $diff" + s"$transactionId was not ${if (shouldBeApplied) "applied" else "failed"}: $snapshot", + s"$transactionId was ${if (shouldBeApplied) "applied" else "failed"}: $snapshot" ) } } diff --git a/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala b/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala index 5c2668e3827..936cbb22f35 100644 --- a/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala +++ b/node/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala @@ -14,7 +14,7 @@ import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.events.UtxEvent import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.history.Domain.BlockchainUpdaterExt -import com.wavesplatform.history.{DefaultWavesSettings, SnapshotOps, randomSig, settingsWithFeatures} +import com.wavesplatform.history.{DefaultWavesSettings, randomSig, settingsWithFeatures} import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.script.Script @@ -1011,7 +1011,7 @@ class UtxPoolSpecification extends FreeSpec with MockFactory with BlocksTransact } "cleanup" - { - "doesnt take the composite diff into account" in withDomain() { d => + "doesnt take the composite snapshot into account" in withDomain() { d => d.helpers.creditWavesToDefaultSigner(11.waves) val transfers = Seq.fill(10)(TxHelpers.transfer(amount = 10.waves)) transfers.foreach(tx => d.utxPool.addTransaction(tx, verify = false)) @@ -1024,7 +1024,7 @@ class UtxPoolSpecification extends FreeSpec with MockFactory with BlocksTransact val transfer1 = TxHelpers.transfer(amount = 10.waves) val transfer2 = TxHelpers.transfer(amount = 10.waves) // Double spend - d.utxPool.priorityPool.setPriorityDiffs(Seq(SnapshotOps.fromDiff(d.createDiff(transfer1), d.blockchain).explicitGet())) + d.utxPool.priorityPool.setPriorityDiffs(Seq(d.createDiff(transfer1))) d.utxPool.addTransaction(transfer2, verify = false) d.utxPool.cleanUnconfirmed() diff --git a/node/src/test/scala/com/wavesplatform/utx/UtxPriorityPoolSpecification.scala b/node/src/test/scala/com/wavesplatform/utx/UtxPriorityPoolSpecification.scala index 1d6970800ad..a4d174b353f 100644 --- a/node/src/test/scala/com/wavesplatform/utx/UtxPriorityPoolSpecification.scala +++ b/node/src/test/scala/com/wavesplatform/utx/UtxPriorityPoolSpecification.scala @@ -107,7 +107,7 @@ class UtxPriorityPoolSpecification extends FreeSpec with SharedDomain { domain.utxPool.priorityPool.priorityTransactions shouldBe empty } - "continues packing when priority diff contains no valid transactions" in { + "continues packing when priority snapshot contains no valid transactions" in { val bob = nextKeyPair domain.appendBlock( TxHelpers.transfer(alice, bob.toAddress, 10.02.waves, fee = 0.001.waves), diff --git a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala index b4872df83f3..f5a3e003909 100644 --- a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala +++ b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/Repl.scala @@ -51,13 +51,13 @@ case class Repl( currentState, view, (oldCtx: (CompilerContext, EvaluationContext[Environment, Future])) => - engine.eval(expr, oldCtx._1, oldCtx._2).map { + engine.eval(expr, version, oldCtx._1, oldCtx._2).map { case Left(e) => (Left(e), oldCtx) case Right((r, newCtx)) => (Right(r), newCtx) }: Future[(Either[String, String], (CompilerContext, EvaluationContext[Environment, Future]))] ) - def initLibraries(): Unit = { + private def initLibraries(): Unit = { val libraryState = state( ( initialCtx.compilerContext, @@ -72,7 +72,7 @@ case class Repl( view, oldCtx => new ReplEngine[Id]() - .eval(libraries.mkString("\n"), oldCtx._1, oldCtx._2) + .eval(libraries.mkString("\n"), version, oldCtx._1, oldCtx._2) .fold( e => throw new RuntimeException(e), { case (r, ctx @ (compilerCtx, evaluationCtx)) => diff --git a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala index 270f8230578..927df4b029b 100644 --- a/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala +++ b/repl/shared/src/main/scala/com/wavesplatform/lang/v1/repl/ReplEngine.scala @@ -3,6 +3,7 @@ package com.wavesplatform.lang.v1.repl import cats.Monad import cats.data.EitherT import cats.implicits.* +import com.wavesplatform.lang.directives.values.StdLibVersion import com.wavesplatform.lang.v1.compiler.CompilerContext.VariableInfo import com.wavesplatform.lang.v1.compiler.Terms.EVALUATED import com.wavesplatform.lang.v1.compiler.Types.{FINAL, UNIT} @@ -20,13 +21,14 @@ class ReplEngine[F[_]: Monad] { def eval( expr: String, + version: StdLibVersion, compileCtx: CompilerContext, evalCtx: EvaluationContext[Environment, F] ): F[Either[String, (String, (CompilerContext, EvaluationContext[Environment, F]))]] = { val r = for { parsed <- EitherT.fromEither[F](parse(expr)) - (newCompileCtx, compiled, exprType) <- EitherT.fromEither[F](ExpressionCompiler.applyWithCtx(compileCtx, parsed)) + (newCompileCtx, compiled, exprType) <- EitherT.fromEither[F](ExpressionCompiler.applyWithCtx(compileCtx, version, parsed)) evaluated <- EitherT(evaluator.applyWithCtx(evalCtx, compiled)).leftMap(error => if (error.message.isEmpty) "Evaluation error" else error.message )