Libplanet changelog
Version 0.8.0
Released on February 4, 2020.
Backward-incompatible API changes
- The internal representation for state keys became
string
(wasAddress
). [#368, #774]- The return type of
IStore.GetBlockStates()
method becameIImmutableDictionary<string, IValue>
(wasAddressStateMap
, which was removed too). [#368, #774] - The type of the second parameter of
IStore.SetBlockStates()
method becameIImmutableDictionary<string, IValue>
(wasAddressStateMap
, which was removed too). [#368, #774] - The second parameter of
IStore.IterateStateReferences()
method becamestring key
(wasAddress address
). [#368, #774] - The second parameter of
IStore.StoreStateReference()
method becameIImmutableSet<string> keys
(wasIImmutableSet<Address> addresses
). [#368, #774] IStore.ListAddresses()
method was replaced byIStore.ListStateKeys()
method. [#368, #774]
- The return type of
- Added
Swarm<T>.FindSpecificPeer()
method to find a specific peer given the address. [#570, #580] - Removed
LiteDBStore
class. UseDefaultStore
instead. [#662] - Removed
BlockChain<T>.Contains(TxId)
method. UseIStore.ContainsTransaction(TxId)
instead. [#676] - Renamed
BlockChain<T>.Contains(HashDigest<SHA256>)
method toBlockChain<T>.ContainsBlock(HashDigest<SHA256>)
. [#678] - Removed
BlockChain<T>.Contains(Block<T>)
method. [#678] - Changed semantics of
BlockChain<T>.ContainsBlock(HashDigest<SHA256>)
method andBlockChain<T>[HashDigest<SHA256>]
indexer as lookups only the current chain, not entire storage. [#678] - Added
IStore.ContainsBlock(HashDigest<SHA256>)
method. [#678] - Removed
AddressStateMap
class. [#98, #368, #692, #774]- The return type of
BlockChain<T>.GetState()
method becameIValue
(wasAddressStateMap
). - The return type of
IStore.GetBlockStates()
method becameIImmutableDictionary<string, IValue>
(wasAddressStateMap
). IStore.SetBlockStates()
method became to takeIImmutableDictionary<string, IValue>
instead ofAddressStateMap
.
- The return type of
Swarm<T>.PreloadAsync()
method andSwarm<T>.StartAsync()
method became to takepreloadBlockDownloadFailed
event handler as an argument. [#694]- Added the
genesisBlock
parameter toBlockChain<T>()
constructor. [#688] - Removed
StateReferenceDownloadState
class. [#703] - Removed
BlockStateDownloadState
class. [#703] - Removed
TxReceived
andBlockReceived
AsyncAutoResetEvent
s fromSwarm<T>
. [#705, #725] - Added
workers
optional parameter intoSwarm<T>()
constructor. [#613, #727] Block<T>
class became not to implementISerializable
. [#751]Transaction<T>
class became not to implementISerializable
. [#751]Block<T>.ToBencodex()
became to returnBencodex.Types.Dictionary
. [#751]Transaction<T>.ToBencodex()
became to returnBencodex.Types.Dictionary
. [#751]- Removed
Block<T>.FromBencodex(byte[])
method. [#751] - Removed
Transaction<T>.FromBencodex(byte[])
method. [#751] Block<T>.ToBencodex()
became to take no arguments. [#749, #757]- Removed
Swarm<T>.BroadcastBlocks(IEnumerable<Block<T>>)
method. [#764] StoreExtension.LookupStateReference<T>()
method was replaced byIStore.LookupStateReference<T>()
method. [#722, #774]
Backward-incompatible network protocol changes
- Added
long
-typedoffset
parameter toRecentStates
andGetRecentStates
messages. [#703] - Added
int
-typediteration
parameter toRecentStates
message. [#703] - Added
BlockHeaderMessage
message. [#764]
Backward-incompatible storage format changes
- The introduced
DefaultStore
is incompatible at the file-level with theLiteDBStore
which had existed.DefaultStore
became to take a directory instead of a single file, and it consists of multiple subdirectories and a LiteDB file for indices. [#662, #680]
Added APIs
- Added
DefaultStore
class to replaceLiteDBStore
. [#662] - Added
IStore.ListAllStateReferences<T>()
method. [#701, #703] - Added
IStore.ListStateKeys()
method to replaceIStore.ListAddresses()
method. [#368, #774] - Added
IStore.LookupStateReference<T>()
method to replaceStoreExtension.LookupStateReference<T>()
method. [#368, #722, #774] - Added
BlockChain<T>.Genesis
property. [#688] - Added
BlockChain<T>.MakeGenesisBlock()
static method. [#688] - Added
InvalidGenesisBlockException
class. [#688] - Added
StateDownloadState
class which reports state preloading iteration progress. [#703] - Added
PeerDiscoveryException
class which inheritsSwarmException
class. [#604, #726] - Added
Swarm<T>.Peers
property which returns an enumerable of peers inSwarm<T>
's routing table. [#739] - Added
Block<T>.Serialize()
method which returnsbyte[]
. [#751] - Added
Transaction<T>.Serialize()
method which returnsbyte[]
. [#751] - Added
Block<T>(Bencodex.Types.Dictionary)
constructor. [#751] - Added
Transaction<T>(Bencodex.Types.Dictionary)
constructor. [#751] - Added
Block<T>.Deserialize(byte[])
method. [#751] - Added
Transaction<T>.Deserialize(byte[])
method. [#751] - Added
StoreExtension.Copy(this IStore, IStore)
extension method. [#753] - Added a
HashDigest<SHA256>?
-typedTxHash
property which digests all transactions in the block toBlock<T>
class. [#749, #757] - Added
CryptoConfig
class. [#758] - Added
ICryptoBackend
class. [#758] - Added
DefaultCryptoBackend
class. [#758] - Added
Swarm<T>.BroadcastBlock(Block<T>)
method. [#764] - Added
Swarm<T>.PeerStates
property. [#772] - Added
PeerState
class which represents aPeer
s state in the routing table. [#772] - Added
Swarm<T>.CheckAllPeersAsync()
method. [#772]
Behavioral changes
Swarm<T>
became to compare only peer's addresses instead of public keys to determine if a peer is in routing table or not. [#665]- When
Swarm<T>.PreloadAsync()
method fails to download blocks,Swarm<T>
became to callpreloadBlockDownloadFailed
event handler taken as an argument. If the event handler is not present,Swarm<T>
throwsSwarmException
. [#694] Swarm<T>.PreloadAsync()
became not to sync state references and block states at once. [#703]Swarm<T>
became to print less logs on debug level during sending states. [#706]- Increased
Swarm<T>
's network timeout value, in order to be stable a high latency internet connection. [#709] Swarm<T>.BootstrapAsync()
became to reportPeerDiscoveryException
instead ofSwarmException
directly. [#604, #726]BlockChain<T>.Append()
became to unstage the stagedTransaction<T>
s that have lower nonce than the highest nonce of the same signer's transactions in the same chain, since these virtually never become valid. [#721, #728]Swarm<T>
became not to fill blocks if received block hashes are continuous. [#732]Swarm<T>
became to can process more requests at once by creating TURN relaying proxy concurrently. [#744]Swarm<T>
became to throwInvalidGenesisBlockException
when receiving block from the nodes that have a different genesis block. [#746]Swarm<T>
became to distinguish the starting stages clearly. In other words,Swarm<T>.StartAsync()
became not to callSwarm<T>.PreloadAsync()
. [#735, #760]- The hash of
Block<T>
has changed due to the change in the method of serialization. [#762] Swarm<T>
became to ignore broadcasted block that has lower index than the current tip. [#764]- The way
Swarm<T>
chose peers to spread messages has changed. [#765, #767]- If there are less than 10 peers in the routing table, select all peers.
- If there are more than 10 peers in the routing table, choose one from each bucket, and if the number is less than 10, then select an additional peers so that the total is 10.
Bug fixes
- Fixed a bug where the canonical chain could be deleted if
Swarm<T>
failed to download blocks due to network connection. [#675] - Fixed bug that re-download block from scratch in preloading. [#685]
- Fixed a bug where state references become outdated if a chain is forked and then adding existing blocks into it. [#704]
- Fixed a bug where
Swarm<T>
had exhausted all available sockets. [#709] - Fixed a bug where
Swarm<T>
had infinitely repeated failed requests. [#709] - Fixed a bug where
Swarm<T>
hadn't stopped properly. [#709] - Fixed a bug where
Swarm<T>.BootstrapAsync()
had stopped due to trivial (recoverable)TimeoutException
s. [#715, #716] - Fixed a bug where
BlockChain<T>.GetNextTxNonce()
had returned invalid tx nonce. [#718] - Fixed a bug where mined transactions were staged again. [#719]
- Fixed a bug where
Block<T>.Hash
property,Block<T>.Mine()
method,Block<T>.FromBencodex()
method,Block<T>.ToBencodex()
method,Transaction<T>.Id
property,Transaction<T>.Signature
property,Transaction<T>.Create()
method,Transaction<T>.FromBencodex()
method, andTransaction<T>.ToBencodex()
method had been non-deterministic on someCultureInfo.CurrentCulture
(e.g.,ar_SA
,fr_FR
,th_TH
) so that it had caused network protocol incompatibilities. [#734] - Fixed a bug where the states became empty between the tip of the peer to receive the states and the tip of the downloaded block. [#736]
- Fixed a bug where
Swarm<T>.StartAsync()
had thrownNullReferenceException
whenhost
parameter is present on the outside of NAT. [#744] - Fixed a bug where
Swarm<T>
had failed to request a TURN relay when it has an IPv6 address. [#744] - Fixed a bug where
DefaultStore
had invalid state references cache after fork. [#759] - Fixed a bug where
BlockChain<T>
had rendered and evaluated actions in the genesis block during forking. [#763] - Fixed a
Swam<T>
's bug that someTransaction<T>
s had become excluded from miningBlock<T>
s after reorg from α to β where aTransaction<T>
was once included by aBlock<T>
(α) and not included by an otherBlock<T>
(β) for the sameIndex
due to the latency gap between nodes. [#775]
Version 0.7.0
Released on November 8, 2019.
Backward-incompatible interface changes
- Renamed
minValue
/maxValue
parameters tolowerBound
/upperBound
ofIRandom.Next()
methods. [#555, #558] - Renamed
IStore.IterateIndex()
method toIterateIndexes()
. [#462, #560] Swarm<T>
class became to implementIDisposable
again and should be disposed to clean up its internal resources. [#485]IStore.IterateStateReferences()
method became to receivehighestIndex
,lowestIndex
, andlimit
parameters. [#447, #545]- Reworked
BlockChain<T>.GetStates()
intoGetState()
which takes only oneAddress
instead ofIEnumerable<Address>
. [#510, #563] - Types of
IAction.PlainValue
and states became restricted toBencodex.Types.IValue
. [#541, #552]IAction.LoadPlainValue(IImmutableDictionary<string, object>)
method became replaced byLoadPlainValue(IValue)
.AccountStateGetter
became to returnIValue
, notobject
.- Added
BencodexExtension
static class.
- Removed
BlockChain<T>.Blocks
. [#409, #583] - Removed
BlockChain<T>.Transactions
. [#409, #583] - Removed the
linger
parameter from theSwarm<T>()
constructor, and added thewaitFor
parameter toSwarm<T>.StopAsync()
method instead. [#581] - Removed the
dialTimeout
parameter from theSwarm<T>
() constructor, and added it toSwarm<T>.PreloadAsync()
&Swarm<T>.StartAsync()
methods. [#581] - Removed
broadcast
parameter fromBlockChain<T>.MakeTransaction()
method. [#609] BlockChain<T>
does not implementIReadOnlyList<T>
anymore. [#630]- Added
BlockChain<T>.Count
property. [#630] - Removed
BlockChain<T>.LongCount()
method. [#630] - Removed
BlockChain<T>.Validate()
method. [#630] - Removed
BlockChain<T>.GetEnumerate()
method. [#630] - Removed
BlockPolicyExtension.ValidateBlocks()
method. [#630] IBlockPolicy<T>.GetNextBlockDifficulty()
method became to receiveBlockChain<T>
instead ofIReadOnlyList<Block<T>>
. [#630]IBlockPolicy<T>.ValidateNextBlock()
method became to receiveBlockChain<T>
instead ofIReadOnlyList<Block<T>>
. [#630]
- Added
Added interfaces
- Added
ProtectedPrivateKey
class. [#577, #614] - Added
IncorrectPassphraseException
class. [#577, #614] - Added
MismatchedAddressException
class. [#577, #614] - Added
KeyJsonException
abstract class. [#577, #614] - Added
InvalidKeyJsonException
class. [#577, #614] - Added
UnsupportedKeyJsonException
class. [#577, #614] - Added
ICipher
interface. [#577, #614] - Added
Aes128Ctr
class. [#577, #614] - Added
IKdf
interface. [#577, #614] - Added
Pbkdf2
class. [#577, #614] - Added
Scrypt
class. [#642, #654] - Added
BlockChain<T>.LongCount()
method. [#575] - Added
BlockChain<T>[HashDigest<T>]
indexer. [#409, #583] - Added
BlockChain<T>.Contains(HashDigest<T>)
method. [#409, #583] - Added
BlockChain<T>.GetTransaction(TxId)
method. [#409, #583] - Added
BlockChain<T>.Contains(TxId)
method. [#409, #583] - Added
ByteUtil.Hex(ImmutableArray<byte>)
overloaded method. [#614] - Added
BlockChain<T>.Contains(Block<T>)
method. [#630] - Added
BlockDownloadState.SourcePeer
property. [#636]
Behavioral changes
- Changed to send
Pong
before updating the message sender to the routing table whenPing
is received. [#566] - Improved performance of
StoreExtension.LookupStateReference<T>()
method. [#447, #545] - Added .NET Core 2.2 as a targeted framework. [#209, #561]
TurnClient.AcceptRelayedStreamAsync()
became to ignore disconnected connections. [#469]Swarm<T>.PreloadAsync()
became to ignore peers with lower tip. [#592]Swarm<T>
became to validate only stale peers. [#568, #593]Swarm<T>
became not to check cached peers immediately after removing peers from its routing table. Instead, it checks cached peers periodically. [#608]- Marked
Address
andHashDigest
as readonly. [#610] IceServer.CreateTurnClient()
became to throwArgumentException
when received invalid url. [#622]Swarm<T>
became to update peer table when receiving messages that are not related with Kademlia protocol. [#594, #627]Swarm<T>
became not to check least recently used peer every time when new peer is fetched. [#627]IAction
became guaranteed that the givenIActionContext.PreviousStates.GetState()
never throwsIncompleteBlockStatesException
. Instead, now it may calculate the incomplete states from the beginning if necessary. [#645]IStore.PutBlock<T>()
became to do nothing when it takes theBlock<T>
more than once. [#647]Swarm<T>.PreloadAsync()
became to try downloading blocks from all neighbor peers, even if any peer among them is unavailable to send blocks. [#636]
Bug fixes
- Fixed a bug where
Swarm<T>
had tried to update a peer infinitely when the peer is evicted from its table. [#566] - Fixed a bug where
Swarm<T>.AppendBlocksAsync()
re-requests blocks that already received when blockchain is empty. [#550, #562] - Fixed a bug that
Swarm<T>
had thrownSocketException
with a messageOperation on non-blocking socket would block
. [#405, #485] - Fixed a bug that accessed all blocks from the genesis block when a swap occurred. [#575]
- Fixed a bug that
Swarm<T>
had thrownInvalidBlockIndexException
during synchronizing with other reorganized peer. [#528, #576] - Fixed a bug where
Swarm<T>
does not render actions in blocks which are filled from other peers. [#579] - Fixed a bug where
Swarm<T>
renders actions in same block multiple times when reorg happens. [#579] LiteDBStore
became to guarantee atomicity of storing blocks. [#584]- Fixed a bug that
BlockChain<T>
had appended a block even if fails to evaluate. [#591] - Fixed a bug where
Swarm<T>
hadn't removed stale peers. [#568, #593, #602] - Fixed a bug that
TurnClient
had thrownIOException
when accepting connection through a TURN relay server. [#453, #599] - Fixed a bug that
KeyNotFoundException
occurred when sending a message through the TurnClient. - Fixed a bug where
BlockChain<T>.GetNextTxNonce
only returned the same nonce when transactions with an old nonce were staged. [#637] - Fixed a bug that
BlockChain<T>
had reset whenSwarm<T>.PreloadAsync()
fails. [#644] - Fixed bug that whole processes could halt when received an invalid type of message. [#628, #641]
- Fixed a bug that received blocks could not be processed if a branch point is a stale block. [#655]
Version 0.6.0
Released on October 1, 2019.
Backward-incompatible interface changes
BlockChain<T>.MineBlock()
is nowasync
and became to throwOperationCanceledException
ifBlockChain<T>
's tip index is changed while mining. [#460, #517]- Users became able to give a cancellation token to
Block<T>.Mine()
andHashcash.Answer()
to cancel the operation. [#460, #517] - Replaced
UnexpectedlyTerminatedTxRehearsalException
withUnexpectedlyTerminatedActionException
. [#498] - The following methods became to throw
UnexpectedlyTerminatedActionException
with having itsInnerException
during actions being evaluated if any action of them throws an exception: [#498]Transaction<T>.EvaluateActions()
Transaction<T>.EvaluateActionsGradually()
Block<T>.EvaluateActionsPerTx()
Block<T>.Evaluate()
BlockChain<T>.GetStates(completeStates: true)
- The concept of "namespaces" in
IStore
was replaced by "chain IDs" to be consistent withBlockChain<T>
. [#483, #486]- Renamed
IStore.ListNamespaces()
method toListChainIds()
. - Renamed
IStore.DeleteNamespace()
method toDeleteChainId()
. - Renamed
IStore.GetCanonicalNamespace()
method toGetCanonicalChainId()
. - Renamed
IStore.SetCanonicalNamespace(string)
method toSetCanonicalChainId(Guid)
. - Replaced
namespace
/sourceNamespace
/destinationNamespace
parameters takingstring
of methods inIStore
andStoreExtension
withchainId
/sourceChainId
/destinationChainId
takingGuid
. - Renamed
NamespaceNotFoundException
toChainIdNotFoundException
. - Replaced
NamespaceNotFoundException(string, string)
constructor withChainIdNotFoundException(Guid, string)
constructor. - Replaced
NamespaceNotFoundException.Namespace
property withChainIdNotFoundException.ChainId
property.
- Renamed
IStore.StoreStateReference<T>(string, IImmutableSet<Address>, Block<T>)
method became replaced byStoreStateReference(Guid, IImmutableSet<Address>, HashDigest<SHA256>, long)
method so that it takes hash and index of a block instead of an entire block. [#420]- Added
IStore.ForkBlockIndexes()
method. [#420] - Removed
addressesToStrip
parameter fromIStore.ForkStateReferences<T>()
method. [#454, #467, #509, #522] - Removed the concept of "staged transactions that should not be broadcasted,"
because its primary usage had been to make a transaction of a reward action
for a candidate for block miner, and the case became achieved through
IBlockPolicy<T>.BlockAction
property which was introduced at 0.5.0. All staged transactions became broadcasted. [#319, #470]BlockChain<T>.StageTransactions(IDictionary<Transaction<T>, bool>)
method became replaced byStageTransactions(IImmutableSet<Transaction<T>>)
.- Removed
toBroadcast
parameter fromIStore.IterateStagedTransactionIds(bool)
method. IStore.StageTransactionIds(IDictionary<TxId, bool>)
method became replaced byStageTransactionIds(IImmutableSet<TxId>()
.
- Removed
Swarm<T>.AddPeersAsync()
method. To connect with seed peers, useSwarm<T>.BootstrapAsync()
method instead. [#353] Peer
with endpoints should be typed asBoundPeer
which is inherited fromPeer
. [#353]- Removed
IActionContext.NewGuid()
method. To get a randomly generated Guid, useRandomExtension.GenerateRandomGuid()
which implements RFC 4122 instead. [#508]
Added interfaces
- Added
BlockChain<T>.TipChanged
event which is invoked with an argument ofBlockChain<T>.TipChangedEventArgs
whenBlockChain<T>.Tip
is changed. [#517, #526] - Added
BlockChain<T>.TipChangedEventArgs
class. [#526] - Added
Swarm<T>.BootstrapAsync()
method to connect with seed peers. [#353] - Added
RandomExtension
static class. [#508] TxId
class became to implementIComparable<TxId>
andIComparable
interfaces. [#244, #511]
Behavioral changes
Swarm<T>
now broadcasts transactions as soon as new transactions are received. [#463, #496]Swarm<T>
now ignores block hashes which already exists. [#461, #484]Swarm<T>.PreloadAsync()
method became to download precalculated states of blocks from a likely branchpoint instead of a genesis block from a trusted peer (i.e.,trustedStateValidators
) where there are branches between peers. [#465, #481]Swarm<T>
's internalGetRecentStates
message became to takeBlockLocator
, an internal data type to approximates a path of a chain of blocks for heuristics to search a likely branchpoint, instead ofHashDigest<SHA256>
. [#465, #481]- NetMQ instances are now initialized at
Swarm<T>.StartAsync()
instead ofSwarm<T>()
. [#353] - Peers now connected via Kademlia protocol. Peers are now selectively connected to each peer. [#353]
TxId
s andBlock
s are now broadcasted to selected peers from routing table of the host peer. [#353]PolymorphicAction<T>.ToString()
method became to show the runtime type of itsInnerAction
for the sake of easier debugging. [#512]- The order of
Block<T>.Transactions
became to be determined by both aBlock<T>.Hash
and aTransaction<T>.Id
, so that signers cannot predict the order of transactions in a block before it's mined. If there are multiple transactions signed by the same signer in a block these transactions become grouped together and the order is determined by aBlock<T>.Hash
and a fingerprint derived from all these transactions, and transactions in each group (per signer) are ordered byTransaction<T>.Nonce
. [#244, #355, #511, #520] LiteDBStore()
became to create the database in memory if thepath
parameter isnull
. [#521]
Bug fixes
- Fixed a bug that
Swarm<T>
hadn't released its TURN related resources onSwarm<T>.StopAsync()
. [#450] - Fixed a bug that
IActionContext.Random
had been possible to generated equivalent results between actions of different transactions in aBlock<T>
. [#519] - Fixed a bug where a forked chain would not be deleted when an exception occurred during fetching block from other peers. [#527, #537, #540]
Version 0.5.3
Released on September 9, 2019.
Bug fixes
- Fix bug where
IAccountStateDelta.GetState()
hadn't returned proper state when the block action is evaluated. [#500]
Version 0.5.2
Released on August 29, 2019.
Bug fixes
- Fixed a bug that
Swarm<T>.PreloadAsync()
method had thrownLiteException
(or other exception depending onIStore
), which indicates a state reference is duplicate, wheretrustedStateValidators
is present and a miner tries to download precalculated states from a trusted peer. [#465, #474] - Fixed a bug tag
Swarm<T>.StartAsync()
sometimes had thrown an exception fromIStore
(e.g.,NullReferenceException
) during broadcasting transactions. [#352, #476]
Version 0.5.1
Released on August 28, 2019.
Bug fixes
- Fixed a bug that
ArgumentNullException
had been thrown when a blockchain, which consists of incomplete states (i.e., precalculated states downloaded from trusted peers), encounters a new branch so that reorg is made. [#454, #466] - Fixed a bug that unnecessarily received all blocks in multiple miner situations. [#457, #468]
Version 0.5.0
Released on August 22, 2019.
Backward-incompatible interface changes
- Added
IStore.GetBlockIndex()
method. [#385] StoreExtension.LookupStateReference<T>()
method became to returnTuple<HashDigest<SHA256>, long>
which is a nullable tuple ofBlock<T>.Hash
andBlock<T>.Index
. [#350]- Added
IBlockPolicy<T>.BlockAction
property. [#319, #367] - Removed the type parameter of
ActionEvaluation
. [#319, #367] ActionEvaluation.Action
became toIAction
type. [#319, #367]LiteDBStore()
constructor became to have a new option namedflush
and turned on by default. [#387, LiteDB #1268]LiteDBStore()
constructor became to have a new option namedreadOnly
and turned off by default. [#434]BaseIndex.ContainsKey()
method becameabstract
. [#390]BlockDownloadState.TotalBlockCount
andBlockDownloadState.ReceivedBlockCount
became toInt64
type. [#396, #399]IStore.IterateIndex()
method became to receiveoffset
andlimit
parameters. [[#425]]- Added
IStore.GetCanonicalNamespace()
method. [#426] - Added
IStore.SetCanonicalNamespace()
method. [#426] - Removed
IRandom.NextDouble()
method, because floating-point arithmetics, which is underspecified, likely introduce indeterminism. [#410, #419] - Added
IActionContext.NewGuId()
method. [#371, #439] Address(byte[])
became to throwArgumentNullException
instead ofNullReferenceException
. [#443]- Removed
FileStore
class. [#446]
Added interfaces
- Added
trustedStateValidators
option toSwarm<T>.PreloadAsync()
method. If any peer in this set is reachable and there is no built up blockchain in a current node,Swarm<T>
receives the latest states of the major blockchain from that trusted peer, which is also calculated by that peer, instead of autonomously calculating the states from scratch. Note that this option is intended to be exposed to end users through a feasible user interface so that they can decide whom to trust for themselves. [#272, #343] - Added
StoreExtension.ListAllStateReferences(this IStore, string, HashDigest<SHA256>?, HashDigest<SHA256>?)
extension method. [#363, #384, #385] Address
class became to implementIComparable<Address>
andIComparable
interfaces. [#363]- Added
BlockChain<T>.BlockHashes
property. [#389] Swarm<T>.PreloadAsync(IProgress<PreloadState>, IImmutableSet<Address>, CancellationToken)
became to report progress for all phases. [#397, #400]- Added
PreloadState
,ActionExecutionState
,StateReferenceDownloadState
, andBlockStateDownloadState
classes to cover all phases in the entire preloading process. [#397, #400] - Added
Address(ImmutableArray<byte>)
constructor. [#442, #443]
Behavioral changes
BlockChain<T>.PreloadAsync()
method became to omit rendering ofIAction
s in the preloaded behind blocks. [#272, #343]Swarm<T>
became to have two more message types:GetRecentStates
(0x0b
) andRecentStates
(0x0c
). [#272, #343]BlockChain<T>.MineBlock()
andBlockChain<T>.GetNextTxNonce()
methods became to ignore transactions that didn't followTransaction<T>.Nonce
sequentially and treat them as pendings. [#365]BlockChain<T>
became to evaluateIBlockPolicy<T>.BlockAction
and set the state when a block is appended to the chain. [#319, #367]BlockSet<T>.ContainsKey()
andTransactionSet<T>.ContainsKey()
methods became O(1) time complexity through omitting iteration and relying own retrieve implementations. [#390]- The way
LiteDBStore
stores state references became efficient, but the file-level backward compatibility was also broken. [#395, #398] Swarm<T>.PreloadAsync()
method became to report a block downloading progress with the total number of blocks to download in the entire batch, instead of the window size of a chunk (i.e., 500). [#396, #399]Swarm<T>.PreloadAsync()
became to get the first parameter,progress
, which acceptsIProgress<PreloadState>
. [#397, #400]BlockHashes
messages became to contain one more higher hash. [#408, #445]Swarm<T>.PreloadAsync()
became safe from data corruption even if a preloading process suddenly gets shutdown. [#417]FileStore
andLiteDBStore
became to guarantee atomicity of storing transactions. [#413]IStore.PutTransaction<T>()
became to do nothing when it takes theTransaction<T>
more than once. [#413]BlockChain<T>.Swap()
became to omit common block finding whenrender
isfalse
. [#423]PrivateKey(byte[])
constructor became to check validity. [#438]
Bug fixes
- Fixed a bug where the
LiteDBStore.IterateStagedTransactionIds()
returns duplicated transaction ids. [#366] - Fixed a bug that
NullReferenceException
occurred when serializing defaultAddress
. [#369] - Removed unnecessary mutex in
Swarm<T>
to avoid continuous delays in peer registration in some situations. [#375] - Fixed a bug that
TurnClient
had thrownKeyNotFoundException
andIOException
on startup. [#377, #378] - Fixed a
LiteDBStore
bug that blocks or transactions had got corrupted sometimes. Instead,LiteDBStore.GetTransaction()
became possible to returnnull
even for already stored transactions, and for that case, a warning will be logged through Serilog. [#386, #387, LiteDB #1268] - Fixed a bug that
NetworkStreamProxy.StartAsync()
hadn't stopped properly when the connection had reset by a remote peer. [#414] - Fixed a bug that
Swarm<T>
had hung forever after a remote peer had disconnected while receiving. [#416] - Fixed a bug that
Swarm<T>.PreloadAsync()
had been processed even if there is no appropriate peer. [#418] - Fixed a bug that TURN-related tasks hadn't restarted automatically when an exception occurred. [#422]
- Fixed a bug that TURN relay connection had disconnected when preloading took a long time. [#424]
Version 0.4.1
Released on July 11, 2019.
Bug fixes
- Fixed a bug where the
BlockChain<T>.GetStates()
method had not returned the latest state when there are multiple addresses. [#346]
Version 0.4.0
Released on July 8, 2019.
Backward-incompatible interface changes
Peer.AppProtocolVersion
became nullable to representPeer
whose version is unknown. [#280]- Added
IStore.ListAddresses()
method. [#272, #285] - Added
IStore.ListTxNonces()
method. [#272, #309, #310] - Removed
BlockChain<T>.GetNonce()
method. [#294] BlockChain<T>.StageTransactions
became to receiveIDictionary<Transaction<T>, bool>
instead ofISet<Transaction<T>>
. [#274, #297]IStore.StageTransactionIds()
method became to receiveIDictionary<TxId, bool>
instead ofISet<TxId>
. [#274, #297]IStore.IterateStagedTransactionIds()
method became to receivebool toBroadcast
which is whether to iterate only the TxId set to broadcast. [#274, #297]Swarm<T>.StartAsync()
method became to receivebroadcastTxInterval
(ormillisecondsBroadcastTxInterval
) parameter. [#274, #297]IStore
became to treat a "tx nonce" mere along
integer instead of a stack of block hashes. [#272, #307, #309, #310]IStore.IncreaseTxNonce<T>(string, Block<T>)
method was replaced byIStore.IncreaseTxNonce(string, Address, long)
method.- Removed
IStore.ForkTxNonce()
method. FileStore
became to occupy fewer bytes for storing tx nonces. This change broke file-level backward compatibility.
IStore
became possible to look up multiple state references in a stack. [#272, #307]- Removed
IStore.LookupStateReference<T>()
method. Instead, a newly added static classStoreExtension
provides an extension method of the same name. - Added
IStore.IterateStateReferences()
method.
- Removed
Swarm
became to have type parameterT
to represent an action type as like asBlockChain<T>
. [#324]Swarm<T>
constructor became to receiveBlockChain<T>
. [#324]- Methods in
Swarm<T>
that had taken a parameter ofBlockChain<T>
type became to neither longer takeBlockChain<T>
nor a generic method. Because theSwarm<T>
constructor takes it instead. [#324] Swarm<T>
does not implementICollection<Peer>
anymore. [#326]- Added
IStore.DeleteNamespace()
method. [#329] - Removed the
id
parameter from theBlockChain<T>
constructor, and it became to automatically detect an appropriateBlockChain<T>.Id
. [#279, #332]
Added interfaces
- Added
LiteDBStore
backend that uses LiteDB under the hood. [#269] - All
*Async()
methods belonging toTurnClient
class became to havecancellationToken
option. [#287] - Added a
Peer
constructor omittingappProtocolVersion
parameter to create aPeer
whose version is unknown. [#280] - Added
IncompleteBlockStatesException
class. [#272, #285] - Added
completeStates
option toBlockChain<T>.GetStates()
method. [#272, #285] - Added
BlockChain<T>.MakeTransaction(PrivateKey, IEnumerable<T>, IImmutableSet<Address>, DateTimeOffset?)
method. [#294] - Added
BlockChain<T>.GetNextTxNonce()
method which counts staged transactions too during nonce computation. [#270, #294] - Added
StoreExtension
static class. [#272, #307] - Added
Swarm<T>.BlockChain
property. [#272, #343]
Behavioral changes
BlockChain<T>.GetStates()
method became to throwIncompleteBlockStatesException
if itsStore
lacks the states of a block that a requested address lastly updated. [#272, #285]- A message
Swarm<T>
makes became to have multiple blocks within it, which means round trips on the network are now much reduced. [#273, #276] Message.Block
has been replaced byMessage.Blocks
and the magic number has been changed to0x0a
. [#276]- Improved performance of
Swarm<T>
's response time toGetBlockHashes
request messages. [#277] - Added IPv6 support to
Libplanet.Stun.StunAddress
. [#267, #271] IStore.GetBlockStates()
became able to returnnull
to represent an absence of states (i.e., incomplete states). [#272, #285]Swarm<T>
became to broadcast stagedTransaction
s periodically so that game apps no more need to maintain their own thread to broadcast staged transactions. [#274, #297]- Previously,
Swarm<T>
had sent an emptyGetTxs
message when it receives an emptyTxIds
from peers, and it had made the network waste bandwidth for unnecessary messages.Swam<T>
became to no more send such emptyGetTxs
. [#297] BlockChain<T>.Swap()
became to delete an index, tx nonces, and state references in the replaced chain. [#329]- Reduced the memory footprint of
BlockChain<T>.FindBranchPoint()
method under the circumstances that the height of theBlockChain<T>
object is high. [#282, #299]
Bug fixes
- Fixed a bug that
Swarm<T>
reportedTaskCanceledException
as an unknown exception while stopping. [#275] - Fixed a bug that
Swarm<T>
didn't stop properly duringSwarm<T>.PreloadAsync()
. [#275] - Fixed a bug where the oldest
TxNonce
of an address is not invalidated when forking usingFileStore.ForkTxNonce()
method. [#281] - Fixed a bug where
LiteDBStore.GetTxNonce()
method throws aSystem.IO.IOException
after forking. [#281] - Fixed a bug that
TurnClient
had not stopped properly. [#287] - Fixed a bug that
TurnClient
had been trying to use an already closed connection. [#303, #308] - Fixed a bug that
KeyNotFoundException
had been thrown instead ofArgumentOutOfRangeException
whenBlockchain<T>[int]
called while the index of a block that does not exist locally. [#208, #317] - Fixed a bug that
Swarm<T>
had not dial to other peer afterSwarm<T>.PreloadAsync()
. [#311] - Fixed an issue where unknown exceptions occurred when
Swarm<T>
receiving a message. [#321, #327]
Version 0.3.0
Released on May 31, 2019.
Backward-incompatible interface changes
- Added
IAction.Render(IActionContext, IAccountStateDelta)
method. [#31, #212] - Added
IAction.Unrender(IActionContext, IAccountStateDelta)
method. [#31, #212] BlockChain<T>.Validate()
method became to receiveIReadOnlyList<Block<T>>
instead ofIEnumerable<Block<T>>
. [#205]IBlockPolicy<T>.GetNextBlockDifficulty()
method became to receiveIReadOnlyList<Block<T>>
instead ofIEnumerable<Block<T>>
. [#205]- Added
IBlockPolicy<T>.ValidateNextBlock(IReadOnlyList<Block<T>>, Block<T>)
method. [#210] - Removed
IBlockPolicy<T>.ValidateBlocks()
method. [#210] BlockChain<T>[int]
became to throwArgumentOutOfRangeException
instead ofIndexOutOfRangeException
. [#210]- Removed
KeyEquals()
methods from all classes and structs. [#216] Swarm
class now does not implementIEquatable<Swarm>
anymore and itsEquals(object)
method andGetHashCode()
method became to have default behavior ofobject
class. [#216]- Also,
Swarm
class now does not implementIDisposable
too. ThusSwarm.Dispose()
was removed too. [#218] Swarm
became to use a queue to maintain internal messages. [#218]- The broadcasting methods are no more
async
, so they are renamed as below.Swarm.BroadcastBlocksAsync()
→Swarm.BroadcastBlocks()
Swarm.BroadcastTxsAsync()
→Swarm.BroadcastTxs()
- The broadcasting methods are no more
- The type of
Block<T>.Difficulty
is changed tolong
instead ofint
, and related classes method parameters and field types have changed accordingly. - Removed
HashDigest.HasLeadingZeroBits()
method. [#213] - The signature of
IStore.PutBlock<T>(Block<T>)
method was changed toPutBlock<T>(Block<T>, Address)
. [#189, #197] Block<T>.Hash
is no longer calculated using the full data of theTransaction<T>
, but is calculated using only theTransaction<T>.Id
. [#234]- Added
IStore.LookupStateReference<T>(string, Address, Block<T>)
method. [#232] - Added
IStore.StoreStateReference<T>(string, Block<T>)
method. [#232] - Added
IStore.ForkStateReferences<T>(string, string, Block<T>, IImmutableSet<Address>
method. [#232] - Removed
Block<T>.Validate()
andBlock<T>.EvaluateActions()
method. [#243] - Added
Transaction<T>.Nonce
andRawTransaction.Nonce
properties. [#246] - Added
IStore.GetTxNonce(string, Address)
method. [#246] - Added
IStore.IncreaseTxNonce<T>(string, Block<T>)
method. [#246] - Added
IStore.ForkTxNonce<T>(string, string, Block<T>, IImmutableSet<Address>
method. [#246]
Added interfaces
BlockChain<T>
became to implementIReadOnlyList<Block<T>>
. [#205]- Added
Swarm.DifferentVersionPeerEncountered
event handler that can handle events when a different version of a peer is discovered. [#167], [#185] - Added
Peer.AppProtocolVersion
property. [#185] - Added
Swarm.PreloadAsync()
method to explicitly and preemptively download initial blocks beforeSwarm.StartAsync<T>()
being called. [#204], [#206] - Added
BlockDownloadState
class to represent a block downloading state. [#204], [#206] - Added
BlockPolicyExtension.ValidateBlocks<T>(IBlockPolicy<T>, IReadOnlyList<Block<T>>, DateTimeOffset)
method. [#210] - Added
Transaction<T>.EvaluateActionsGradually(HashDigest<SHA256>, long, IAccountStateDelta, Address, bool)
method. [#31, #212] - Added
Block<T>.EvaluateActionsPerTx(AccountStateGetter)
method. [#31, #212] - Added
HashDigest.Satisfies()
method. [#213] BlockPolicy<T>
constructor became to receive theminimumDifficulty
and the miningdifficultyBoundDivisor
. [#213]- Added
BlockChain<T>.UnstageTransactions()
method. [#223] Swarm
constructor became to receive alinger
(ormillisecondsLinger
) parameter. This purposes to determine how long to wait for pending messages when aSwarm
instance is requested to terminate.- Added
NamespaceNotFoundException
class. [#232] - Added
Block<T>.Evaluate()
method. [#243] - Made
InvalidBlockTimestampException
classpublic
so that it can be caught. [#133, #251] - Added
InvalidTxNonceException
class. [#246]
Behavioral changes
Swarm.StartAsync()
now receives the height of blocks (tipIndex
) from other known peers and synchronizes the blocks if necessary before propagating/receiving pinpointed recent blocks to prevent inefficient round-trips. [#187, #190]- The calculation algorithm of
BlockPolicy<T>.GetNextBlockDifficulty()
method was changed to the Ethereum Homestead algorithm except for the difficulty bomb. [#213] - The difficulty was changed from representing the number of leading zeros of target number to representing a divisor to obtain the target number. [#213]
BlockSet<T>[int]
changed so as not to validate a block. [#231]- Improved read performance of
Block<T>.Hash
andTransaction<T>.Id
. [#228, #234] Swarm.StartAsync()
now does not callSwarm.StopAsync()
anymore, thereforeSwarm.StopAsync()
should be explicitly called. [#236]Transaction<T>.EvaluateActionsGradually()
became to recordIAccountStateDelta.SetState()
calls even if its argument is the same to the previous state. [#241]Block<T>.Validate()
andBlock<T>.EvaluateActions()
are integrated intoBlock<T>.Evaluate()
. [#243]BlockChain<T>.Append()
became to executeAction.Execute()
only once per action in theBlock<T>
. [#243]BlockChain<T>.Append()
method became to throwInvalidTxNonceException
when theTransaction<T>.Nonce
does not correspond to itsSigner
's current nonce. [#246]Swarm
became to enforceForceDotNet.Force()
in AsyncIO while it's running on Mono runtime. [#247]
Bug fixes
- Fixed a bug that TURN relay had been disconnected when being connected for longer than 5 minutes. [#198]
- Fixed a bug that
Swarm
had attempted to use TURN relay even if thehost
argument was given. [#198] - Improved the read throughput of
BlockChain<T>.Append()
. - Improved overall read throughput of
BlockChain<T>
while blocks are being mined byBlockChain<T>.MineBlock()
. [#191] - Fixed a bug that
TurnClientException
had been thrown by Swarm when a STUN nonce is stale. [#193] - Fixed
BlockChain<T>.GetStates()
had descended to the bottom (i.e., the genesis block) where a givenAddress
refers to a nonexistent account (i.e., never used before). [#189, #192] - Fixed a bug that a TURN connection had turned unavailable after it once failed to parse a message (due to a corrupted packet). [#215]
- Instead of validating the entire blocks,
BlockChain<T>.Append()
method became to validate only the next block to be appended. [#210] - Improved
BlockChain<T>.Fork()
performance by avoiding double validation of already validated blocks. [#215] - Removed unnecessary writer locks on
BlockChain<T>.StageTransactions()
. [#217] - Improved concurrency of
BlockChain<T>.Append()
method by removing unnecessary race conditions. [#217] - Fixed a bug that
Swarm
could not properly communicate withPeer
behind NAT. [#240] - Fixed a bug that
BlockChain<T>.FindNextHashes()
throwsArgumentOutOfRangeException
when chain is empty. - Fixed a bug that
TurnClient.AcceptRelayedStreamAsync()
didn't handle concurrent connections correctly. [#256]
Version 0.2.2
Released on April 12, 2019.
- Fixed a bug that
BlockChain<T>.GetStates()
had returned slower than necessary for many addresses. [#189, #192]
Version 0.2.1
Released on April 7, 2019.
This version purposes to entirely replace 0.2.0, because a wrong .nupkg file was uploaded to 0.2.0 on NuGet Gallery. Note that 0.2.0 on NuGet Gallery was unlisted.
Version 0.2.0
Released on April 5, 2019.
PrivateKey.Decrypt()
now throws anInvalidCiphertextException
instead of returningnull
whencipherText
is invalid. [#140]Transaction<T>
'sSender
–Recipient
model was replaced bySigner
–UpdatedAddresses
model. Unlike cryptocurrencies, transactions in games are not necessarily a transfer of assets, so it is difficult to determine what type of assert is transferred or who will receives the asset. A more useful perspective is, like what kind of transformation is performed, or what states are changed. To be close to this perspective, we decided to get rid ofTransaction<T>.Recipient
(which is singular) and haveTransaction<T>.UpdatedAddresses
(which is plural) instead. As there is no more asset to transfer, the termSender
was also renamed toSigner
, which fits more to the new perspective. [#121]- Renamed
Transaction<T>.Sender
,RawTransaction.Signer
, andIActionContext.From
properties toSigner
. The corresponding parameter names on constructors and methods were also renamed too. - Old
Transaction<T>.Make()
factory method is replaced by newTransaction<T>.Create()
factory method. Thetimestamp
parameter became optional, and the new optionalupdatedAddresses
parameter was added. - Removed
IActionContext.To
property. Transaction<T>.Recipient
andRawTransaction.Recipient
properties were replaced byTransaction<T>.UpdatedAddresses
andRawTransaction.UpdatedAddresses
properties. The corresponding parameter names on constructors and methods were replaced too.- Since the schema of
RawTransaction
class was changed, the serialization format of transactions and blocks were also changed. It affects to the way to generateTransaction<T>.Signature
,Transaction<T>.Id
, andBlock.Hash
values as well. - Added
InvalidTxUpdatedAddressesException
exception class. - A nullary overload of
Block<T>.Validate()
method was gone so that the block validation API is always time-wise. Instead,Block<T>.Validate()
method now has only one overload:Validate(DateTimeOffset, AccountStateGetter)
returningIAccountStateDelta
. Block<T>.Validate()
andBlockChain<T>.Validate()
methods now can throw anInvalidTxUpdateAddressesException
.
- Renamed
The
Address
esIAction
tries to update no more need to be manually coded usingIAction.RequestStates()
method. That method was removed at all, and updatedAddress
es became automatically determined (for the most cases) by track "dirties" on rehearsal mode. This mode dry-runsIAction
s with emptyIActionContext.PreviousStates
. [#121]- Added
AccountStateGetter
delegate to provide a read-only view to account states. - Added
IAccountStateDelta
interface to replaceAddressStateMap
. The interface purposes to provide a read-write view to account states with maintainingUpdatedAddresses
(so-called "dirty"). [#98] - The type of
IActionContext.PreviousStates
property was changed fromAddressStateMap
toIAccountStateDelta
. - Added
IActionContext.Rehearsal
property. [#131, #135] - Added
UnexpectedlyTerminatedTxRehearsalException
class. [#132, #136] - The return type of
IAction.Execute()
method was changed fromAddressStateMap
toIAccountStateDelta
. - Removed
IAction.RequestStates()
method because there is no need for it and thus it is not used anymore. - Added
Transaction<T>.EvaluateActions()
method. - Added
Block<T>.EvaluateActions()
generator method.
- Added
The built-in subtype polymorphism of
IAction
andTransaction<T>
was moved to a separated newPolymorphicAction<T>
abstract class. Polymorphic actions now should be wrapped byPolymorphicAction<T>
. For example, the following code:public abstract class AbstractAction : IAction { ... } [ActionType("attack")] public sealed class Attack : AbstractAction { ... } [ActionType("sleep")] public sealed class Sleep : AbstractAction { ... }
var tx = Transaction<AbstractAction>.Create( ..., actions: new[] { new Attack(...), ... } );
should be changed to like:
var tx = Transaction<PolymorphicAction<AbstractAction>>.Create( ..., actions: new[] { new PolymorphicAction<AbstractAction>(new Attack(...)), ... } );
It can be simpler by implicit casting:
var tx = Transaction<PolymorphicAction<AbstractAction>>.Create( ..., actions: new PolymorphicAction<AbstractAction>[] { new Attack(...), } );
[#169]
- The type parameter
T
ofTransaction<T>
,Block<T>
, andBlockChain<T>
became to require having apublic
parameterless constructor (i.e.,new()
) besides implementingIAction
interface. This means anabstract class
or aninterface
no more can be passed toT
, but only a concreteclass
or astruct
can be passed.
- The type parameter
Fixed a bug that mutating a collection of
IAction
s passed to constructors or factory methods ofTransaction<T>
had affected made instances as well. The type ofTransaction<T>.Actions
property was changed fromIList<T>
toIImmutableList<T>
. The corresponding parameters on constructors and factory methods also were changed to takeIEnumerable<T>
instead ofIList<T>
.InvalidTxException
and its subclasses became to haveTxId
property and the corresponding constructor parameter. This can be useful when multipleTransaction<T>
objects are validated at once.Added
Address.Size
constant, which is fixed to theInt32
20.Fixed a bug that
Block<T>.Validate()
had not thrownInvalidTxException
even if there is any integrity error on itsTransactions
.Improved the write throughput of
BlockChain<T>
while pollingBlockChain<T>.GetStates()
Swarm.AddPeersAsync()
was fixed so that unreachablePeer
s are ignored. [#128]Swarm
became able to relay their connection via TURN (RFC 5766) to NAT traversal. To enable this, its constructor (Swarm()
) takes the newly addedIceServer
s as configuration.Since we decided to depend on TURN (RFC 5766) and STUN (RFC 5389) to work around NAT so that
Peer
's endpoints don't have to be multiple,Peer.Urls
was renamed toPeer.EndPoint
and its type also was changed fromIImmutableList<Uri>
toDnsEndPoint
. [#120, #123 by Yang Chun Ung, #126, #127, #165, #166]Swarm
became to ignore tip blocks of the same height (Index
) that it already has and deal with only longer (higher) blocks.Fixed a bug that occurred when
Swarm
was handling multiple responses at the same time.Fixed a bug that the
Swarm
constructor had hanged in certain runtimes like Unity engine.Removed
AddressTransactionSet
which handles handleAddress
toIEnumerable<TxId>
indices, and the following methods inIStore
:IStore.IterateAddresses()
IStore.GetAddressTransactionIds()
IStore.AppendAddressTransactionId()
IStore.CountAddresses()
Added
IStore.ListNamespaces()
method.IStore.CountBlocks()
andIStore.CountTransactions()
became to returnlong
.Block/tx-related methods in
IStore
andBaseIndex<T>
no longer accepts@namespace
parameter. It means that even if a forking occurs, the same block/tx files are shared.Fixed a bug that made unnecessary fork when receiving blocks from other peer.
Action classes that implement
IAction
but lackActionTypeAttribute
became reported byPolymorphicAction<T>
throwingMissingActionTypeException
at runtime. [#28, #144, #169]Turn into parameter in
BlockPolicy
's constructor to milliseconds. [#151]BencodexFormatter
became able to serializeBigInteger
. [#159]Made
Swarm
possible to configure its networkappProtocolVersion
and, to ignore peers if their version is different. [#167], [#170]Renamed
Block<T>.RewardBeneficiary
toBlock<T>.Miner
. [#174]Added
BlockChain<T>.Blocks
property. [#176]Added
BlockChain<T>.Transactions
property. [#176]
Version 0.1.1
Released on March 5, 2019.
- Improved stability of
Swarm
andSwarmTest
.
Version 0.1.0
Initial release. Released on February 26, 2019.