Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ import org.ergoplatform.wallet.boxes.{ErgoBoxAssetExtractor, ErgoBoxSerializer}
import org.ergoplatform.wallet.interpreter.TransactionHintsBag
import org.ergoplatform.wallet.protocol.context.InputContext
import org.scalacheck.Gen
import sigma.util.BenchmarkUtil
import scorex.crypto.hash.Blake2b256
import scorex.util.encode.Base16
import sigma.{Colls, VersionContext}
import sigma.ast.ErgoTree.DefaultHeader
Expand Down Expand Up @@ -326,34 +324,29 @@ class ErgoNodeTransactionSpec extends ErgoCorePropertyTest with ErgoCompilerHelp

property("transaction with too many inputs should be rejected") {

//we assume that verifier must finish verification of any script in less time than 250K hash calculations
// (for the Blake2b256 hash function over a single block input)
val Timeout: Long = {
val hf = Blake2b256

//just in case to heat up JVM
(1 to 5000000).foreach(i => hf(s"$i-$i"))

val t0 = System.currentTimeMillis()
(1 to 250000).foreach(i => hf(s"$i"))
val t = System.currentTimeMillis()
t - t0
}
// This test used to calibrate a wall-clock budget by timing 250K Blake2b256 hashes and then
// assert that validation fits in it. On a loaded CI machine the calibration window and the
// measurement window get different shares of the CPU, so the assertions flipped at random
// (see issue #2095). What the node is actually protected by is the block cost limit, not the
// clock, so the assertions below are on cost, which is deterministic.

val gen = validErgoTransactionGenTemplate(0, 0, 2000, trueLeafGen)
val (from, tx) = gen.sample.get
tx.statelessValidity().isSuccess shouldBe true

//check that spam transaction is being rejected quickly
implicit val verifier: ErgoInterpreter = ErgoInterpreter(parameters)
val (validity, time0) = BenchmarkUtil.measureTime(tx.statefulValidity(from, IndexedSeq(), emptyStateContext))

// with the block cost limit in force, the spam transaction is rejected, and validation is
// aborted as soon as the accumulated cost passes the limit
val validity = tx.statefulValidity(from, IndexedSeq(), emptyStateContext)
validity.isSuccess shouldBe false
assert(time0 <= Timeout)

val cause = validity.failed.get.getMessage
cause should startWith(ValidationRules.errorMessage(bsBlockTransactionsCost, "", emptyModifierId, ErgoTransaction.modifierTypeId).take(30))

//check that spam transaction validation with no cost limit is indeed taking too much time
// with a cost limit high enough to let it through, the same transaction validates, and the cost
// it accumulates is far above the block limit - that gap is what makes it spam, and it does not
// depend on how fast the machine running the test is
import Parameters._
val maxCost = (Int.MaxValue - 10) / 10 // cannot use Int.MaxValue directly due to overflow when it is converted to block cost
val ps = Parameters(0, DefaultParameters.updated(MaxBlockCostIncrease, maxCost), emptyVSUpdate)
Expand All @@ -365,11 +358,15 @@ class ErgoNodeTransactionSpec extends ErgoCorePropertyTest with ErgoCompilerHelp
Array.fill(3)(0.toByte),
ErgoValidationSettingsUpdate.empty,
0.toByte)
val (_, time) = BenchmarkUtil.measureTime(
tx.statefulValidity(from, IndexedSeq(), sc)(verifier)
)

assert(time > Timeout)
val blockCostLimit = emptyStateContext.currentParameters.maxBlockCost
val fullCost = tx.statefulValidity(from, IndexedSeq(), sc)(verifier).get
// the generator produces exactly `maxInputs` inputs, so this ratio is stable (~4.4x as of today)
fullCost.toLong should be > (blockCostLimit.toLong * 2)

// and it is rejected by any limit below its cost, one unit below included
val justBelow = stateContextWith(Parameters(0, DefaultParameters.updated(MaxBlockCostIncrease, fullCost - 1), emptyVSUpdate))
tx.statefulValidity(from, IndexedSeq(), justBelow)(ErgoInterpreter(justBelow.currentParameters)) shouldBe 'failure
}

property("transaction cost") {
Expand Down
Loading