diff --git a/it/src/main/scala/com/wavesplatform/it/Node.scala b/it/src/main/scala/com/wavesplatform/it/Node.scala index df9af9490d7..40bb65f4ed5 100644 --- a/it/src/main/scala/com/wavesplatform/it/Node.scala +++ b/it/src/main/scala/com/wavesplatform/it/Node.scala @@ -7,7 +7,7 @@ import com.wavesplatform.account.{PrivateKeyAccount, PublicKeyAccount} import com.wavesplatform.it.util.GlobalTimer import com.wavesplatform.settings.WavesSettings import com.wavesplatform.state.EitherExt2 -import com.wavesplatform.transaction.FeeCalculator +import com.wavesplatform.state.diffs.CommonValidation import com.wavesplatform.utils.{Base58, LoggerFacade} import org.asynchttpclient.Dsl.{config => clientConfig, _} import org.asynchttpclient._ @@ -45,7 +45,7 @@ object Node { def publicKeyStr = Base58.encode(n.publicKey.publicKey) - def fee(txTypeId: Byte): Long = FeeCalculator.FeeConstants(txTypeId) + def fee(txTypeId: Byte): Long = CommonValidation.FeeConstants(txTypeId) def blockDelay: FiniteDuration = n.settings.blockchainSettings.genesisSettings.averageBlockDelay } diff --git a/src/main/scala/com/wavesplatform/Application.scala b/src/main/scala/com/wavesplatform/Application.scala index 6526cf978cf..0a6e86038b1 100644 --- a/src/main/scala/com/wavesplatform/Application.scala +++ b/src/main/scala/com/wavesplatform/Application.scala @@ -29,7 +29,6 @@ import com.wavesplatform.network.RxExtensionLoader.RxExtensionLoaderShutdownHook import com.wavesplatform.network._ import com.wavesplatform.settings._ import com.wavesplatform.state.appender.{BlockAppender, CheckpointAppender, ExtensionAppender, MicroblockAppender} -import com.wavesplatform.transaction._ import com.wavesplatform.utils.{NTP, ScorexLogging, SystemInformationReporter, Time, forceStopApplication} import com.wavesplatform.utx.{MatcherUtxPool, UtxPool, UtxPoolImpl} import com.wavesplatform.wallet.Wallet @@ -98,12 +97,11 @@ class Application(val actorSystem: ActorSystem, val settings: WavesSettings, con if (wallet.privateKeyAccounts.isEmpty) wallet.generateNewAccounts(1) - val feeCalculator = new FeeCalculator(blockchainUpdater) val time: Time = NTP val establishedConnections = new ConcurrentHashMap[Channel, PeerInfo] val allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE) val innerUtxStorage = - new UtxPoolImpl(time, blockchainUpdater, feeCalculator, settings.blockchainSettings.functionalitySettings, settings.utxSettings) + new UtxPoolImpl(time, blockchainUpdater, settings.blockchainSettings.functionalitySettings, settings.utxSettings) matcher = if (settings.matcherSettings.enable) { val m = new Matcher(actorSystem, diff --git a/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala b/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala index ff8c9c7990a..63e1cd5340b 100644 --- a/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala +++ b/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala @@ -23,6 +23,23 @@ object CommonValidation { val MaxTimePrevBlockOverTransactionDiff: FiniteDuration = 2.hours val ScriptExtraFee = 400000L + val FeeConstants: Map[Byte, Long] = Map( + GenesisTransaction.typeId -> 0, + PaymentTransaction.typeId -> 1, + IssueTransaction.typeId -> 1000, + ReissueTransaction.typeId -> 1000, + BurnTransaction.typeId -> 1, + TransferTransaction.typeId -> 1, + MassTransferTransaction.typeId -> 1, + LeaseTransaction.typeId -> 1, + LeaseCancelTransaction.typeId -> 1, + ExchangeTransaction.typeId -> 3, + CreateAliasTransaction.typeId -> 1, + DataTransaction.typeId -> 1, + SetScriptTransaction.typeId -> 10, + SponsorFeeTransaction.typeId -> 1000 + ) + def disallowSendingGreaterThanBalance[T <: Transaction](blockchain: Blockchain, settings: FunctionalitySettings, blockTime: Long, @@ -135,24 +152,20 @@ object CommonValidation { case _ => Right(tx) } - private def feeInUnits(blockchain: Blockchain, height: Int, tx: Transaction): Either[ValidationError, Long] = tx match { - case _: GenesisTransaction => Right(0) - case _: PaymentTransaction => Right(1) - case _: IssueTransaction => Right(1000) - case _: ReissueTransaction => Right(1000) - case _: BurnTransaction => Right(1) - case _: TransferTransaction => Right(1) - case tx: MassTransferTransaction => Right(1 + (tx.transfers.size + 1) / 2) - case _: LeaseTransaction => Right(1) - case _: LeaseCancelTransaction => Right(1) - case _: ExchangeTransaction => Right(3) - case _: CreateAliasTransaction => Right(1) - case tx: DataTransaction => - val base = if (blockchain.isFeatureActivated(BlockchainFeatures.SmartAccounts, height)) tx.bodyBytes() else tx.bytes() - Right(1 + (base.length - 1) / 1024) - case _: SetScriptTransaction => Right(10) - case _: SponsorFeeTransaction => Right(1000) - case _ => Left(UnsupportedTransactionType) + private def feeInUnits(blockchain: Blockchain, height: Int, tx: Transaction): Either[ValidationError, Long] = { + FeeConstants + .get(tx.builder.typeId) + .map { baseFee => + tx match { + case tx: MassTransferTransaction => + baseFee + (tx.transfers.size + 1) / 2 + case tx: DataTransaction => + val base = if (blockchain.isFeatureActivated(BlockchainFeatures.SmartAccounts, height)) tx.bodyBytes() else tx.bytes() + baseFee + (base.length - 1) / 1024 + case _ => baseFee + } + } + .toRight(UnsupportedTransactionType) } def getMinFee(blockchain: Blockchain, fs: FunctionalitySettings, height: Int, tx: Transaction): Either[ValidationError, (Option[AssetId], Long)] = { diff --git a/src/main/scala/com/wavesplatform/transaction/FeeCalculator.scala b/src/main/scala/com/wavesplatform/transaction/FeeCalculator.scala deleted file mode 100644 index e2f917781a3..00000000000 --- a/src/main/scala/com/wavesplatform/transaction/FeeCalculator.scala +++ /dev/null @@ -1,66 +0,0 @@ -package com.wavesplatform.transaction - -import com.wavesplatform.settings.FunctionalitySettings -import com.wavesplatform.state._ -import com.wavesplatform.transaction.FeeCalculator._ -import com.wavesplatform.transaction.ValidationError.InsufficientFee -import com.wavesplatform.transaction.assets._ -import com.wavesplatform.transaction.assets.exchange.ExchangeTransaction -import com.wavesplatform.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} -import com.wavesplatform.transaction.smart.SetScriptTransaction -import com.wavesplatform.transaction.transfer._ - -class FeeCalculator(blockchain: Blockchain) { - - private val Kb = 1024 - - def enoughFee[T <: Transaction](tx: T, blockchain: Blockchain, fs: FunctionalitySettings): Either[ValidationError, T] = - if (blockchain.height >= Sponsorship.sponsoredFeesSwitchHeight(blockchain, fs)) Right(tx) - else enoughFee(tx) - - def enoughFee[T <: Transaction](tx: T): Either[ValidationError, T] = { - val (txFeeAssetId, txFeeValue) = tx.assetFee - val minFeeForTx = minFeeFor(tx) - txFeeAssetId match { - case None => - Either - .cond( - txFeeValue >= minFeeForTx, - tx, - InsufficientFee(s"Fee for ${tx.builder.classTag} transaction does not exceed minimal value of $minFeeForTx") - ) - case Some(_) => Right(tx) - } - } - - private def minFeeFor(tx: Transaction): Long = { - val baseFee = FeeConstants(tx.builder.typeId) - tx match { - case tx: DataTransaction => - val sizeInKb = 1 + (tx.bytes().length - 1) / Kb - baseFee * sizeInKb - case tx: MassTransferTransaction => - val transferFee = FeeConstants(TransferTransactionV1.typeId) - transferFee + baseFee * tx.transfers.size - case _ => baseFee - } - } -} - -object FeeCalculator { - val FeeConstants = Map( - PaymentTransaction.typeId -> 100000, - IssueTransaction.typeId -> 100000000, - TransferTransaction.typeId -> 100000, - MassTransferTransaction.typeId -> 50000, - ReissueTransaction.typeId -> 100000, - BurnTransaction.typeId -> 100000, - ExchangeTransaction.typeId -> 300000, - LeaseTransaction.typeId -> 100000, - LeaseCancelTransaction.typeId -> 100000, - CreateAliasTransaction.typeId -> 100000, - DataTransaction.typeId -> 100000, - SetScriptTransaction.typeId -> 100000, - SponsorFeeTransaction.typeId -> 100000000 - ) -} diff --git a/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala b/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala index fcb135205c5..749befd2fac 100644 --- a/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala +++ b/src/main/scala/com/wavesplatform/utx/UtxPoolImpl.scala @@ -29,7 +29,7 @@ import scala.collection.JavaConverters._ import scala.concurrent.duration.DurationLong import scala.util.{Left, Right} -class UtxPoolImpl(time: Time, blockchain: Blockchain, feeCalculator: FeeCalculator, fs: FunctionalitySettings, utxSettings: UtxSettings) +class UtxPoolImpl(time: Time, blockchain: Blockchain, fs: FunctionalitySettings, utxSettings: UtxSettings) extends ScorexLogging with Instrumented with AutoCloseable @@ -189,7 +189,6 @@ class UtxPoolImpl(time: Time, blockchain: Blockchain, feeCalculator: FeeCalculat _ <- checkScripted(b, tx) _ <- checkAlias(b, tx) _ <- canReissue(b, tx) - _ <- feeCalculator.enoughFee(tx, blockchain, fs) diff <- TransactionDiffer(fs, blockchain.lastBlockTimestamp, time.correctedTime(), blockchain.height)(b, tx) } yield { pessimisticPortfolios.add(tx.id(), diff) diff --git a/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala b/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala index 7ff6a1337d9..e5401a2c349 100644 --- a/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala +++ b/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala @@ -24,7 +24,6 @@ import com.wavesplatform.{NoShrink, TransactionGen, crypto} import org.scalacheck.Gen import org.scalatest.prop.PropertyChecks import org.scalatest.{Inside, Matchers, PropSpec} -import com.wavesplatform.OrderOps._ class ExchangeTransactionDiffTest extends PropSpec with PropertyChecks with Matchers with TransactionGen with Inside with NoShrink { @@ -246,8 +245,7 @@ class ExchangeTransactionDiffTest extends PropSpec with PropertyChecks with Matc forAll(allValidP) { case (genesis, transfers, issueAndScripts, etx) => - val smallFee = CommonValidation.ScriptExtraFee + FeeCalculator.FeeConstants(etx.builder.typeId) - 1 - + val smallFee = CommonValidation.ScriptExtraFee + CommonValidation.FeeConstants(ExchangeTransaction.typeId) val exchangeWithSmallFee = ExchangeTransactionV2 .create( MATCHER, diff --git a/src/test/scala/com/wavesplatform/transaction/FeeCalculatorSpecification.scala b/src/test/scala/com/wavesplatform/transaction/FeeCalculatorSpecification.scala deleted file mode 100644 index e148dac7599..00000000000 --- a/src/test/scala/com/wavesplatform/transaction/FeeCalculatorSpecification.scala +++ /dev/null @@ -1,101 +0,0 @@ -package com.wavesplatform.transaction - -import com.wavesplatform.TransactionGen -import com.wavesplatform.account.Address -import com.wavesplatform.state._ -import com.wavesplatform.transaction.assets._ -import com.wavesplatform.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} -import com.wavesplatform.transaction.smart.script.Script -import com.wavesplatform.transaction.transfer._ -import org.scalamock.scalatest.MockFactory -import org.scalatest.prop.PropertyChecks -import org.scalatest.{Assertion, Matchers, PropSpec} - -class FeeCalculatorSpecification extends PropSpec with PropertyChecks with Matchers with TransactionGen with MockFactory { - - implicit class ConditionalAssert(v: Either[_, _]) { - - def shouldBeRightIf(cond: Boolean): Assertion = { - if (cond) { - v shouldBe an[Right[_, _]] - } else { - v shouldBe an[Left[_, _]] - } - } - } - - property("Transfer transaction ") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(transferV1Gen) { tx: TransferTransactionV1 => - if (tx.feeAssetId.isEmpty) { - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 100000) - } else { - feeCalc.enoughFee(tx) shouldBe an[Right[_, _]] - } - } - } - - property("Payment transaction ") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(paymentGen) { tx: PaymentTransaction => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 100000) - } - } - - property("Issue transaction ") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(issueGen) { tx: IssueTransaction => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 100000000) - } - } - - property("Reissue transaction ") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(reissueGen) { tx: ReissueTransaction => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 200000) - } - } - - property("Burn transaction ") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(burnGen) { tx: BurnTransaction => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 300000) - } - } - - property("Lease transaction") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(leaseGen) { tx: LeaseTransaction => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 400000) - } - } - - property("Lease cancel transaction") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(leaseCancelGen) { tx: LeaseCancelTransaction => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 500000) - } - } - - property("Create alias transaction") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(createAliasGen) { tx: CreateAliasTransaction => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= 600000) - } - } - - property("Data transaction") { - val feeCalc = new FeeCalculator(noScriptBlockchain) - forAll(dataTransactionGen) { tx => - feeCalc.enoughFee(tx) shouldBeRightIf (tx.fee >= Math.ceil(tx.bytes().length / 1024.0) * 100000) - } - } - - private def createBlockchain(accountScript: Address => Option[Script]): Blockchain = { - val r = stub[Blockchain] - (r.accountScript _).when(*).onCall((addr: Address) => accountScript(addr)).anyNumberOfTimes() - r - } - - private def noScriptBlockchain: Blockchain = createBlockchain(_ => None) -} diff --git a/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala b/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala index 8520e8a24fb..73cf0b6d682 100644 --- a/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala +++ b/src/test/scala/com/wavesplatform/utx/UtxPoolSpecification.scala @@ -19,7 +19,7 @@ import com.wavesplatform.transaction.smart.script.Script import com.wavesplatform.transaction.smart.script.v1.ScriptV1 import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer import com.wavesplatform.transaction.transfer._ -import com.wavesplatform.transaction.{FeeCalculator, Transaction} +import com.wavesplatform.transaction.Transaction import com.wavesplatform.utils.Time import org.scalacheck.Gen import org.scalacheck.Gen._ @@ -71,20 +71,16 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with .label("transferWithRecipient") private def massTransferWithRecipients(sender: PrivateKeyAccount, recipients: List[PublicKeyAccount], maxAmount: Long, time: Time) = { - import FeeCalculator.FeeConstants - val amount = maxAmount / (recipients.size + 1) val transfers = recipients.map(r => ParsedTransfer(r.toAddress, amount)) val txs = for { version <- Gen.oneOf(MassTransferTransaction.supportedVersions.toSeq) - minFee = FeeConstants(TransferTransaction.typeId) + FeeConstants(MassTransferTransaction.typeId) * transfers.size + minFee = CommonValidation.FeeConstants(TransferTransaction.typeId) + CommonValidation.FeeConstants(MassTransferTransaction.typeId) * transfers.size fee <- chooseNum(minFee, amount) } yield MassTransferTransaction.selfSigned(version, None, sender, transfers, time.getTimestamp(), fee, Array.empty[Byte]).explicitGet() txs.label("transferWithRecipient") } - private def mkCalculator(blockchain: Blockchain) = new FeeCalculator(blockchain) - private val stateGen = for { sender <- accountGen.label("sender") senderBalance <- positiveLongGen.label("senderBalance") @@ -106,7 +102,6 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with new UtxPoolImpl( time, bcu, - mkCalculator(bcu), FunctionalitySettings.TESTNET, UtxSettings(10, 10.minutes, Set.empty, Set.empty, 5.minutes, allowTransactionsFromSmartAccounts = true) ) @@ -123,7 +118,6 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with new UtxPoolImpl( time, bcu, - mkCalculator(bcu), FunctionalitySettings.TESTNET, UtxSettings(10, 1.minute, Set.empty, Set.empty, 5.minutes, allowTransactionsFromSmartAccounts = true) ) @@ -138,7 +132,7 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with txs <- Gen.nonEmptyListOf(transferWithRecipient(sender, recipient, senderBalance / 10, time)) } yield { val settings = UtxSettings(10, 1.minute, Set.empty, Set.empty, 5.minutes, allowTransactionsFromSmartAccounts = true) - val utxPool = new UtxPoolImpl(time, bcu, mkCalculator(bcu), FunctionalitySettings.TESTNET, settings) + val utxPool = new UtxPoolImpl(time, bcu, FunctionalitySettings.TESTNET, settings) txs.foreach(utxPool.putIfNew) (sender, bcu, utxPool, time, settings) }).label("withValidPayments") @@ -150,7 +144,7 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with txs <- Gen.nonEmptyListOf(transferWithRecipient(sender, recipient, senderBalance / 10, time)) // @TODO: Random transactions } yield { val settings = UtxSettings(10, 1.minute, Set(sender.address), Set.empty, 5.minutes, allowTransactionsFromSmartAccounts = true) - val utxPool = new UtxPoolImpl(time, bcu, mkCalculator(bcu), FunctionalitySettings.TESTNET, settings) + val utxPool = new UtxPoolImpl(time, bcu, FunctionalitySettings.TESTNET, settings) (sender, utxPool, txs) }).label("withBlacklisted") @@ -162,7 +156,7 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with } yield { val settings = UtxSettings(txs.length, 1.minute, Set(sender.address), Set(recipient.address), 5.minutes, allowTransactionsFromSmartAccounts = true) - val utxPool = new UtxPoolImpl(time, bcu, mkCalculator(bcu), FunctionalitySettings.TESTNET, settings) + val utxPool = new UtxPoolImpl(time, bcu, FunctionalitySettings.TESTNET, settings) (sender, utxPool, txs) }).label("withBlacklistedAndAllowedByRule") @@ -176,7 +170,7 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with } yield { val whitelist: Set[String] = if (allowRecipients) recipients.map(_.address).toSet else Set.empty val settings = UtxSettings(txs.length, 1.minute, Set(sender.address), whitelist, 5.minutes, allowTransactionsFromSmartAccounts = true) - val utxPool = new UtxPoolImpl(time, bcu, mkCalculator(bcu), FunctionalitySettings.TESTNET, settings) + val utxPool = new UtxPoolImpl(time, bcu, FunctionalitySettings.TESTNET, settings) (sender, utxPool, txs) }).label("massTransferWithBlacklisted") @@ -188,7 +182,7 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with val time = new TestTime() forAll(listOfN(count, transfer(sender, senderBalance / 2, time))) { txs => - val utx = new UtxPoolImpl(time, bcu, mkCalculator(bcu), FunctionalitySettings.TESTNET, utxSettings) + val utx = new UtxPoolImpl(time, bcu, FunctionalitySettings.TESTNET, utxSettings) f(txs, utx, time) } } @@ -206,7 +200,6 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with val utx = new UtxPoolImpl( time, bcu, - mkCalculator(bcu), FunctionalitySettings.TESTNET, UtxSettings(10, offset.millis, Set.empty, Set.empty, 5.minutes, allowTransactionsFromSmartAccounts = true) ) @@ -244,7 +237,6 @@ class UtxPoolSpecification extends FreeSpec with Matchers with MockFactory with val utx = new UtxPoolImpl( new TestTime(), bcu, - mkCalculator(bcu), smartAccountsFs, UtxSettings(10, 1.day, Set.empty, Set.empty, 1.day, allowTransactionsFromSmartAccounts = scEnabled) )