Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine creating and staging transaction interface #294

Merged
merged 2 commits into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ To be released.
- `Peer.AppProtocolVersion` became nullable to represent `Peer` whose version
is unknown.
- Added `IStore.ListAddresses()` method. [[#272], [#285]]
- Renamed `BlockChain<T>.GetNonce()` method to `BlockChain<T>.GetNextTxNonce()`
method. [[#294]]

### Added interfaces

Expand All @@ -22,6 +24,8 @@ To be released.
- Added `IncompleteBlockStatesException` class. [[#272], [#285]]
- Added `completeStates` option to `BlockChain<T>.GetStates()` method.
[[#272], [#285]]
- Added `BlockChain<T>.MakeTransaction(PrivateKey, IEnumerable<T>,
IImmutableSet<Address>, DateTimeOffset?)` method. [[#294]]

### Behavioral changes

Expand Down Expand Up @@ -65,6 +69,7 @@ To be released.
[#281]: https://github.com/planetarium/libplanet/pull/281
[#285]: https://github.com/planetarium/libplanet/pull/285
[#287]: https://github.com/planetarium/libplanet/pull/287
[#294]: https://github.com/planetarium/libplanet/pull/294


Version 0.3.0
Expand Down
75 changes: 65 additions & 10 deletions Libplanet.Tests/Blockchain/BlockChainTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ public void ForkTxNonce()
_fx.MakeTransaction(actions, privateKey: privateKey),
};

Assert.Equal(0, _blockChain.GetNonce(address));
Assert.Equal(0, _blockChain.GetNextTxNonce(address));

Block<DumbAction> b1 = TestUtils.MineNext(
genesis,
Expand All @@ -497,7 +497,7 @@ public void ForkTxNonce()
_blockChain.Policy.GetNextBlockDifficulty(_blockChain));
_blockChain.Append(b1);

Assert.Equal(1, _blockChain.GetNonce(address));
Assert.Equal(1, _blockChain.GetNextTxNonce(address));

Transaction<DumbAction>[] txsB =
{
Expand All @@ -513,10 +513,10 @@ public void ForkTxNonce()
_blockChain.Policy.GetNextBlockDifficulty(_blockChain));
_blockChain.Append(b2);

Assert.Equal(2, _blockChain.GetNonce(address));
Assert.Equal(2, _blockChain.GetNextTxNonce(address));

BlockChain<DumbAction> forked = _blockChain.Fork(b1.Hash);
Assert.Equal(1, forked.GetNonce(address));
Assert.Equal(1, forked.GetNextTxNonce(address));
}

[Fact]
Expand Down Expand Up @@ -863,18 +863,18 @@ public void EvaluateActions()
}

[Fact]
public void GetNonce()
public void GetNextTxNonce()
{
var privateKey = new PrivateKey();
Address address = privateKey.PublicKey.ToAddress();
var actions = new[] { new DumbAction(_fx.Address1, "foo") };

Assert.Equal(0, _blockChain.GetNonce(address));
Assert.Equal(0, _blockChain.GetNextTxNonce(address));

Block<DumbAction> genesis = TestUtils.MineGenesis<DumbAction>();
_blockChain.Append(genesis);

Assert.Equal(0, _blockChain.GetNonce(address));
Assert.Equal(0, _blockChain.GetNextTxNonce(address));

Transaction<DumbAction>[] txsA =
{
Expand All @@ -888,7 +888,7 @@ public void GetNonce()
_blockChain.Policy.GetNextBlockDifficulty(_blockChain));
_blockChain.Append(b1);

Assert.Equal(1, _blockChain.GetNonce(address));
Assert.Equal(1, _blockChain.GetNextTxNonce(address));

Transaction<DumbAction>[] txsB =
{
Expand All @@ -898,7 +898,7 @@ public void GetNonce()

_blockChain.StageTransactions(txsB.ToHashSet());

Assert.Equal(3, _blockChain.GetNonce(address));
Assert.Equal(3, _blockChain.GetNextTxNonce(address));

Transaction<DumbAction>[] txsC =
{
Expand All @@ -907,7 +907,62 @@ public void GetNonce()
};
_blockChain.StageTransactions(txsC.ToHashSet());

Assert.Equal(4, _blockChain.GetNonce(address));
Assert.Equal(4, _blockChain.GetNextTxNonce(address));
}

[Fact]
public void ValidateNonce()
{
var privateKey = new PrivateKey();
var actions = new[] { new DumbAction() };

Block<DumbAction> genesis = TestUtils.MineGenesis<DumbAction>();
_blockChain.Append(genesis);

Transaction<DumbAction>[] txsA =
{
_fx.MakeTransaction(actions, privateKey: privateKey, nonce: 1),
_fx.MakeTransaction(actions, privateKey: privateKey, nonce: 0),
};
Block<DumbAction> b1 = TestUtils.MineNext(genesis, txsA);
_blockChain.ValidateNonce(b1);

Transaction<DumbAction>[] txsB =
{
_fx.MakeTransaction(actions, privateKey: privateKey, nonce: 1),
};
Block<DumbAction> b2 = TestUtils.MineNext(genesis, txsB);
Assert.Throws<InvalidTxNonceException>(() =>
_blockChain.ValidateNonce(b2));
}

[Fact]
public void MakeTransaction()
{
var privateKey = new PrivateKey();
Address address = privateKey.PublicKey.ToAddress();
var actions = new[] { new DumbAction(address, "foo") };

_blockChain.MakeTransaction(privateKey, actions);
_blockChain.MakeTransaction(privateKey, actions);

List<Transaction<DumbAction>> txs = _blockChain.Store
.IterateStagedTransactionIds()
.Select(_blockChain.Store.GetTransaction<DumbAction>)
.OrderBy(tx => tx.Nonce)
.ToList();

Assert.Equal(2, txs.Count());

var transaction = txs[0];
Assert.Equal(0, transaction.Nonce);
Assert.Equal(address, transaction.Signer);
Assert.Equal(actions, transaction.Actions);

transaction = txs[1];
Assert.Equal(1, transaction.Nonce);
Assert.Equal(address, transaction.Signer);
Assert.Equal(actions, transaction.Actions);
}

/// <summary>
Expand Down
57 changes: 50 additions & 7 deletions Libplanet/Blockchain/BlockChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Libplanet.Action;
using Libplanet.Blockchain.Policies;
using Libplanet.Blocks;
using Libplanet.Crypto;
using Libplanet.Store;
using Libplanet.Tx;

Expand All @@ -19,6 +20,7 @@ public class BlockChain<T> : IReadOnlyList<Block<T>>
where T : IAction, new()
{
private readonly ReaderWriterLockSlim _rwlock;
private readonly object _txLock;

public BlockChain(IBlockPolicy<T> policy, IStore store)
: this(policy, store, Guid.NewGuid())
Expand All @@ -35,6 +37,7 @@ public BlockChain(IBlockPolicy<T> policy, IStore store, Guid id)

_rwlock = new ReaderWriterLockSlim(
LockRecursionPolicy.SupportsRecursion);
_txLock = new object();
}

~BlockChain()
Expand Down Expand Up @@ -291,7 +294,7 @@ public AddressStateMap GetStates(
/// the <see cref="Policy"/>.</exception>
/// <exception cref="InvalidTxNonceException">Thrown when the
/// <see cref="Transaction{T}.Nonce"/> is different from
/// <see cref="BlockChain{T}.GetNonce"/> result of the
/// <see cref="GetNextTxNonce"/> result of the
/// <see cref="Transaction{T}.Signer"/>.</exception>
public void Append(Block<T> block) =>
Append(block, DateTimeOffset.UtcNow);
Expand All @@ -312,7 +315,7 @@ public void Append(Block<T> block) =>
/// the <see cref="Policy"/>.</exception>
/// <exception cref="InvalidTxNonceException">Thrown when the
/// <see cref="Transaction{T}.Nonce"/> is different from
/// <see cref="BlockChain{T}.GetNonce"/> result of the
/// <see cref="GetNextTxNonce"/> result of the
/// <see cref="Transaction{T}.Signer"/>.</exception>
public void Append(Block<T> block, DateTimeOffset currentTime) =>
Append(block, currentTime, render: true);
Expand Down Expand Up @@ -348,7 +351,14 @@ public void UnstageTransactions(ISet<Transaction<T>> transactions)
transactions.Select(tx => tx.Id).ToImmutableHashSet());
}

public long GetNonce(Address address)
/// <summary>
/// Gets next <see cref="Transaction{T}.Nonce"/> of the address.
/// </summary>
/// <param name="address">The <see cref="Address"/> from which to obtain the
/// <see cref="Transaction{T}.Nonce"/> value.</param>
/// <returns>The next <see cref="Transaction{T}.Nonce"/> value of the
/// <paramref name="address"/>.</returns>
public long GetNextTxNonce(Address address)
{
long nonce = Store.GetTxNonce(Id.ToString(), address);

Expand Down Expand Up @@ -380,11 +390,9 @@ DateTimeOffset currentTime
@namespace,
index - 1
);
List<Transaction<T>> transactions = Store
IEnumerable<Transaction<T>> transactions = Store
.IterateStagedTransactionIds()
.Select(Store.GetTransaction<T>)
.OrderBy(tx => tx.Nonce)
.ToList();
.Select(Store.GetTransaction<T>);

Block<T> block = Block<T>.Mine(
index: index,
Expand All @@ -402,6 +410,41 @@ DateTimeOffset currentTime
public Block<T> MineBlock(Address miner) =>
MineBlock(miner, DateTimeOffset.UtcNow);

/// <summary>
/// Creates a new <see cref="Transaction{T}"/> and stage the transaction.
/// </summary>
/// <param name="privateKey">A <see cref="PrivateKey"/> of the account who creates and
/// signs a new transaction.</param>
/// <param name="actions">A list of <see cref="IAction"/>s to include to a new transaction.
/// </param>
/// <param name="updatedAddresses"><see cref="Address"/>es whose states affected by
/// <paramref name="actions"/>.</param>
/// <param name="timestamp">The time this <see cref="Transaction{T}"/> is created and
/// signed.</param>
/// <returns>A created new <see cref="Transaction{T}"/> signed by the given
/// <paramref name="privateKey"/>.</returns>
/// <seealso cref="Transaction{T}.Create" />
public Transaction<T> MakeTransaction(
PrivateKey privateKey,
IEnumerable<T> actions,
IImmutableSet<Address> updatedAddresses = null,
DateTimeOffset? timestamp = null)
{
timestamp = timestamp ?? DateTimeOffset.UtcNow;
lock (_txLock)
{
Transaction<T> tx = Transaction<T>.Create(
GetNextTxNonce(privateKey.PublicKey.ToAddress()),
privateKey,
actions,
updatedAddresses,
timestamp);
StageTransactions(new HashSet<Transaction<T>> { tx });

return tx;
}
}

internal void Append(
Block<T> block,
DateTimeOffset currentTime,
Expand Down
16 changes: 16 additions & 0 deletions Libplanet/Blockchain/MineBlockEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Libplanet.Action;
using Libplanet.Blocks;

namespace Libplanet.Blockchain
{
public class MineBlockEventArgs<T>
where T : IAction, new()
{
public MineBlockEventArgs(Block<T> block)
{
Block = block;
}

public Block<T> Block { get; }
}
}
5 changes: 4 additions & 1 deletion Libplanet/Blocks/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ public Block(
Miner = miner;
PreviousHash = previousHash;
Timestamp = timestamp;
Transactions = transactions;

// FIXME: We need to fix this to solve
// https://github.com/planetarium/libplanet/issues/244.
Transactions = transactions.OrderBy(tx => tx.Nonce).ToList();
dahlia marked this conversation as resolved.
Show resolved Hide resolved
Hash = Hashcash.Hash(ToBencodex(false, false));
}

Expand Down
6 changes: 3 additions & 3 deletions Libplanet/Tx/InvalidTxNonceException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Libplanet.Tx
{
/// <summary>
/// The exception that is thrown when the <see cref="Transaction{T}.Nonce"/>
/// is different from <see cref="BlockChain{T}.GetNonce"/> result of
/// is different from <see cref="BlockChain{T}.GetNextTxNonce"/> result of
/// the <see cref="Transaction{T}.Signer"/>.
/// </summary>
[Serializable]
Expand All @@ -19,7 +19,7 @@ public sealed class InvalidTxNonceException : InvalidTxException
/// <param name="txid">The invalid <see cref="Transaction{T}"/>'s
/// <see cref="Transaction{T}.Id"/>. It is automatically included to
/// the <see cref="Exception.Message"/> string.</param>
/// <param name="expectedNonce"><see cref="BlockChain{T}.GetNonce"/>
/// <param name="expectedNonce"><see cref="BlockChain{T}.GetNextTxNonce"/>
/// result of the <see cref="Transaction{T}.Signer"/>.</param>
/// <param name="improperNonce">The actual
/// <see cref="Transaction{T}.Nonce"/>.</param>
Expand All @@ -44,7 +44,7 @@ public InvalidTxNonceException(
}

/// <summary>
/// <see cref="BlockChain{T}.GetNonce"/> result of the
/// <see cref="BlockChain{T}.GetNextTxNonce"/> result of the
/// <see cref="Transaction{T}.Signer"/>.
/// </summary>
public long ExpectedNonce { get; }
Expand Down