diff --git a/CHANGES.md b/CHANGES.md index 499fff8c70d..d944a2cb31d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,11 @@ Version 3.9.3 To be released. +Due to changes in [#3567], a network ran with a prior version may not +be compatible with this version, specifically, those that ran with +[Libplanet 2.0.0] and onwards prior to this release that have included +`Transaction`s that aren't compatible with the updated specification in [#3567]. + - (Libplanet.Explorer) Added `BlockHashType` and `TxIdType`. [[#3559]] - (Libplanet.Explorer) Changed `HashDigestSHA256Type` to `HashDigestType`. [[#3559]] @@ -23,12 +28,15 @@ To be released. - (Libplanet.Explorer) Removed parameters `mysql-server`, `mysql-port`, `mysql-username`, `mysql-password`, and `mysql-database` from `Libplanet.Explorer.Executable`. [[#3564]] + - Changed `TxInvoice` to no longer allow negative values for + `MaxGasPrice` and `GasLimit`. [[#3567]] [#3559]: https://github.com/planetarium/libplanet/pull/3559 [#3560]: https://github.com/planetarium/libplanet/pull/3560 [#3561]: https://github.com/planetarium/libplanet/pull/3561 [#3562]: https://github.com/planetarium/libplanet/pull/3562 [#3564]: https://github.com/planetarium/libplanet/pull/3564 +[#3567]: https://github.com/planetarium/libplanet/pull/3567 Version 3.9.2 diff --git a/Libplanet.Tests/Action/ActionEvaluatorTest.cs b/Libplanet.Tests/Action/ActionEvaluatorTest.cs index 80266f8f775..b990835763d 100644 --- a/Libplanet.Tests/Action/ActionEvaluatorTest.cs +++ b/Libplanet.Tests/Action/ActionEvaluatorTest.cs @@ -1146,68 +1146,6 @@ public void EvaluateThrowingInsufficientBalanceForGasFee() evaluations.Single().Exception?.InnerException?.GetType()); } - [Fact] - public void EvaluateMinusGasFee() - { - var privateKey = new PrivateKey(); - var address = privateKey.Address; - Currency foo = Currency.Uncapped( - "FOO", - 18, - null - ); - - var freeGasAction = new UseGasAction() - { - GasUsage = 0, - Memo = "FREE", - MintValue = FungibleAssetValue.FromRawValue(foo, 10), - Receiver = address, - }; - - var payGasAction = new UseGasAction() - { - GasUsage = 1, - Memo = "CHARGE", - }; - - var store = new MemoryStore(); - var stateStore = new TrieStateStore(new MemoryKeyValueStore()); - var chain = TestUtils.MakeBlockChain( - policy: new BlockPolicy(), - actions: new[] - { - freeGasAction, - }, - store: store, - stateStore: stateStore); - var tx = Transaction.Create( - nonce: 0, - privateKey: privateKey, - genesisHash: chain.Genesis.Hash, - maxGasPrice: FungibleAssetValue.FromRawValue(foo, -10), - gasLimit: 5, - actions: new[] - { - payGasAction, - }.ToPlainValues()); - - chain.StageTransaction(tx); - var miner = new PrivateKey(); - Block block = chain.ProposeBlock(miner); - - var evaluations = chain.ActionEvaluator.Evaluate( - block, chain.Store.GetStateRootHash(block.PreviousHash)); - - Assert.False(evaluations[0].InputContext.BlockAction); - Assert.Single(evaluations); - Assert.NotNull(evaluations.Single().Exception); - Assert.NotNull(evaluations.Single().Exception?.InnerException); - Assert.Equal( - typeof(ArgumentOutOfRangeException), - evaluations.Single().Exception?.InnerException?.GetType()); - } - [Fact] public void GenerateRandomSeed() { diff --git a/Libplanet.Tests/Tx/TxInvoiceTest.cs b/Libplanet.Tests/Tx/TxInvoiceTest.cs index 63c90cf269e..aa33975141d 100644 --- a/Libplanet.Tests/Tx/TxInvoiceTest.cs +++ b/Libplanet.Tests/Tx/TxInvoiceTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Immutable; +using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.Tests.Common; using Libplanet.Crypto; @@ -18,6 +19,57 @@ public class TxInvoiceTest public static readonly Address AddressB = new Address("B61CE2Ce6d28237C1BC6E114616616762f1a12Ab"); + [Fact] + public void ConstructorGasConditions() + { + var random = new System.Random(); + var genesisHash = random.NextBlockHash(); + var timestamp = DateTimeOffset.UtcNow; + var actions = new TxActionList(Array.Empty()); + + _ = new TxInvoice( + genesisHash: genesisHash, + timestamp: timestamp, + actions: actions, + maxGasPrice: null, + gasLimit: null); + _ = new TxInvoice( + genesisHash: genesisHash, + timestamp: timestamp, + actions: actions, + maxGasPrice: Currency.Uncapped("DUMB", 0, null) * 100, + gasLimit: 100); + + Assert.Throws(() => + new TxInvoice( + genesisHash: genesisHash, + timestamp: timestamp, + actions: actions, + maxGasPrice: Currency.Uncapped("DUMB", 0, null) * 100, + gasLimit: null)); + Assert.Throws(() => + new TxInvoice( + genesisHash: genesisHash, + timestamp: timestamp, + actions: actions, + maxGasPrice: null, + gasLimit: 100)); + Assert.Throws(() => + new TxInvoice( + genesisHash: genesisHash, + timestamp: timestamp, + actions: actions, + maxGasPrice: Currency.Uncapped("DUMB", 0, null) * -100, + gasLimit: 100)); + Assert.Throws(() => + new TxInvoice( + genesisHash: genesisHash, + timestamp: timestamp, + actions: actions, + maxGasPrice: Currency.Uncapped("DUMB", 0, null) * 100, + gasLimit: -100)); + } + [Fact] public void PlainConstructor() { diff --git a/Libplanet.Types/Tx/ITxInvoice.cs b/Libplanet.Types/Tx/ITxInvoice.cs index 140be4cbac3..f8a609fae85 100644 --- a/Libplanet.Types/Tx/ITxInvoice.cs +++ b/Libplanet.Types/Tx/ITxInvoice.cs @@ -54,18 +54,39 @@ public interface ITxInvoice : IEquatable TxActionList Actions { get; } /// - /// The maximum amount of that the transaction author is - /// willing to pay for the transaction. - /// This can be if align the handling of that transaction - /// with the . + /// + /// The maximum amount of that the + /// 's author is willing to pay per gas + /// for the . + /// + /// + /// If , gas processing is entirely bypassed. + /// The parity of -ness is always the same as + /// that of . + /// + /// + /// If not , this value cannot be negative. + /// /// + /// + /// FungibleAssetValue? MaxGasPrice { get; } /// - /// The limit of the total amount of gas that the transaction will use. - /// This can be if align the handling of that transaction - /// with the . + /// + /// The limit on the total amount of gas that the can use. + /// + /// + /// If , gas processing is entirely bypassed. + /// The parity of -ness is always the same as + /// that of . + /// + /// + /// If not , this value cannot be negative. + /// /// + /// + /// long? GasLimit { get; } } } diff --git a/Libplanet.Types/Tx/TxInvoice.cs b/Libplanet.Types/Tx/TxInvoice.cs index dbd7eca0a7c..f372ff9be3d 100644 --- a/Libplanet.Types/Tx/TxInvoice.cs +++ b/Libplanet.Types/Tx/TxInvoice.cs @@ -116,12 +116,25 @@ internal TxInvoice( throw new ArgumentNullException(nameof(updatedAddresses)); } - if (maxGasPrice is null ^ gasLimit is null) + switch (maxGasPrice, gasLimit) { - throw new ArgumentException( - $"Either {nameof(maxGasPrice)} (null: {maxGasPrice is null}) and " + - $"{nameof(gasLimit)} (null: {gasLimit is null}) must be both null " + - $"or both non-null."); + case (null, null): + break; + case (null, { }): + case ({ }, null): + throw new ArgumentException( + $"Either {nameof(maxGasPrice)} (null: {maxGasPrice is null}) and " + + $"{nameof(gasLimit)} (null: {gasLimit is null}) must be both null " + + $"or both non-null."); + case ({ } mgp, { } gl): + if (mgp.Sign < 0 || gl < 0) + { + throw new ArgumentException( + $"Both {nameof(maxGasPrice)} ({mgp}) and {nameof(gasLimit)} ({gl}) " + + $"must be non-negative."); + } + + break; } GenesisHash = genesisHash;