Skip to content

Commit

Permalink
Intermediate commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed Jan 6, 2021
1 parent 1b58a2b commit 4e7173c
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 15 deletions.
22 changes: 19 additions & 3 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ To be released.

### Backward-incompatible API changes

- Added the parameter `protocolVersion` to `Block<T>(long, long, BigInteger,
Nonce, Address?, HashDigest<SHA256>?, DateTimeOffset,
IEnumerable<Transaction<T>> transactions, HashDigest<SHA256>?,
HashDigest<SHA256>?)` constructor. [[#1142], [#1146]]
- Added the parameter to `protocolVersion` to `Block<T>.Mine()` method.
[[#1142], [#1146]]
- Added the first parameter `protocolVersion` to `BlockHeader()` constructor.
[[#1142], [#1146]]
- Added `stagePolicy` as the second parameter to `BlockChain<T>()`
constructor. [[#1130], [#1131]]
- Removed `IBlockStatesStore` interface. [[#1117]]
Expand All @@ -30,6 +38,10 @@ To be released.

### Added APIs

- Added `Block<T>.CurrentProtocolVersion` constant. [[#1142], [#1146]]
- Added `Block<T>.ProtocolVersion` property. [[#1142], [#1146]]
- Added `Block<T>.Header` property. [[#1070], [#1102]]
- Added `BlockHeader.ProtocolVersion` property. [[#1142], [#1146]]
- Added `IStagePolicy<T>` interface. [[#1130], [#1131]]
- Added `VolatileStagePolicy<T>` class. [[#1130], [#1131], [#1136]]
- Added `ITransport` interface. [[#1052]]
Expand All @@ -38,11 +50,10 @@ To be released.
- Added `BlockExceedingTransactionsException` class. [[#1104], [#1110]]
- Added `BlockChain<T>.GetStagedTransactionIds()` method. [[#1089]]
- (Libplanet.RocksDBStore) Added `maxTotalWalSize`, `keepLogFileNum` and
`maxLogFileSize` parameters into `RocksDBStore` constructor.
`maxLogFileSize` parameters into `RocksDBStore()` constructor.
[[#1065], [#1102], [#1132]]
- Added `Swarm<T>.BlockDemand` property. [[#1068], [#1102]]
- Added `BlockDemand` struct. [[#1068], [#1102]]
- Added `Block<T>.Header` property. [[#1070], [#1102]]
- Added `TurnClient.PublicAddress` property. [[#1074], [#1102]]
- Added `TurnClient.EndPoint` property. [[#1074], [#1102]]
- Added `TurnClient.BehindNAT` property. [[#1074], [#1102]]
Expand Down Expand Up @@ -70,6 +81,7 @@ To be released.
class. [[#1119]]
- Added `Libplanet.Blockchain.Renderers.Debug.InvalidRenderException<T>`
class. [[#1119]]
- Added `InvalidBlockProtocolVersionException` class. [[#1142], [#1146]]
- Added `InvalidBlockTxHashException` class. [[#1116]]
- Removed `Swarm<T>.TraceTable()` method. [[#1120]]
- Added `Swarm<T>.PeerStates` property. [[#1120]]
Expand All @@ -82,6 +94,9 @@ To be released.
- Upgraded *Bencodex* package (which is a dependency) so that Libplanet gets
benefits from its recent optimizations.
[[#1081], [#1084], [#1086], [#1101]]
- Since `BlockHeader.ProtocolVersion` was added, the existing blocks are
considered protocol compliant with the protocol version zero.
[[#1142], [#1146]]
- When a `BlockChain<T>` follows `VolatileStagePolicy<T>`, which is
Libplanet's the only built-in `IStagePolicy<T>` implementation at
the moment, as its `StagePolicy`, its staged transactions are no longer
Expand All @@ -98,7 +113,6 @@ To be released.
- `Swarm<T>` became not to fill states from trusted peers, because now states
can be validated rather than trusted due to MPT. [[#1117]]
- `HashDigest<SHA256>` became serializable. [[#795], [#1125]]

### Bug fixes

- Fixed a bug where `BlockChain<T>.MineBlock()` was not automatically
Expand Down Expand Up @@ -146,6 +160,8 @@ To be released.
[#1136]: https://github.com/planetarium/libplanet/pull/1136
[#1137]: https://github.com/planetarium/libplanet/pull/1137
[#1141]: https://github.com/planetarium/libplanet/pull/1141
[#1142]: https://github.com/planetarium/libplanet/issues/1142
[#1146]: https://github.com/planetarium/libplanet/pull/1146


Version 0.10.2
Expand Down
40 changes: 40 additions & 0 deletions Libplanet.Tests/Blockchain/BlockChainTest.ValidateNextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,46 @@ public void ValidateNextBlock()
Assert.Equal(_blockChain.Tip, validNextBlock);
}

[Fact]
private void ValidateNextBlockProtocolVersion()
{
Block<DumbAction> block1 = Block<DumbAction>.Mine(
1,
1024,
_fx.GenesisBlock.TotalDifficulty,
_fx.GenesisBlock.Miner.Value,
_fx.GenesisBlock.Hash,
_fx.GenesisBlock.Timestamp.AddDays(1),
_emptyTransaction,
protocolVersion: 1
).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
_blockChain.Append(block1);

Block<DumbAction> block2 = Block<DumbAction>.Mine(
2,
1024,
block1.TotalDifficulty,
_fx.GenesisBlock.Miner.Value,
block1.Hash,
_fx.GenesisBlock.Timestamp.AddDays(1),
_emptyTransaction,
protocolVersion: 0
).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
Assert.Throws<InvalidBlockProtocolVersionException>(() => _blockChain.Append(block2));

Block<DumbAction> block3 = Block<DumbAction>.Mine(
2,
1024,
block1.TotalDifficulty,
_fx.GenesisBlock.Miner.Value,
block1.Hash,
_fx.GenesisBlock.Timestamp.AddDays(1),
_emptyTransaction,
protocolVersion: Block<DumbAction>.CurrentProtocolVersion + 1
).AttachStateRootHash(_fx.StateStore, _policy.BlockAction);
Assert.Throws<InvalidBlockProtocolVersionException>(() => _blockChain.Append(block3));
}

[Fact]
private void ValidateNextBlockInvalidIndex()
{
Expand Down
34 changes: 34 additions & 0 deletions Libplanet.Tests/Blocks/BlockHeaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,40 @@ public class BlockHeaderTest : IClassFixture<BlockFixture>

public BlockHeaderTest(BlockFixture fixture) => _fx = fixture;

[Fact]
public void ValidateProtocolVersion()
{
var header = new BlockHeader(
protocolVersion: -1,
index: 0,
difficulty: _fx.Next.Difficulty,
totalDifficulty: _fx.Next.TotalDifficulty,
nonce: _fx.Next.Nonce.ByteArray,
miner: _fx.Next.Miner?.ByteArray ?? ImmutableArray<byte>.Empty,
hash: _fx.Next.Hash.ByteArray,
txHash: _fx.Next.TxHash?.ByteArray ?? ImmutableArray<byte>.Empty,
previousHash: _fx.Next.PreviousHash?.ByteArray ?? ImmutableArray<byte>.Empty,
timestamp: _fx.Next.Timestamp.ToString(
BlockHeader.TimestampFormat,
CultureInfo.InvariantCulture
),
preEvaluationHash: TestUtils.GetRandomBytes(32).ToImmutableArray(),
stateRootHash: ImmutableArray<byte>.Empty
);

Assert.Throws<InvalidBlockProtocolVersionException>(() =>
header.Validate(DateTimeOffset.UtcNow)
);
}

[Fact]
public void ValidateTimestamp()
{
DateTimeOffset now = DateTimeOffset.UtcNow;
string future = (now + TimeSpan.FromSeconds(16))
.ToString(BlockHeader.TimestampFormat, CultureInfo.InvariantCulture);
var header = new BlockHeader(
protocolVersion: 0,
index: 0,
difficulty: 0,
totalDifficulty: 0,
Expand All @@ -43,6 +70,7 @@ public void ValidateTimestamp()
public void ValidateNonce()
{
var header = new BlockHeader(
protocolVersion: 0,
index: _fx.Next.Index,
difficulty: long.MaxValue,
totalDifficulty: _fx.Genesis.TotalDifficulty + long.MaxValue,
Expand All @@ -67,6 +95,7 @@ public void ValidateNonce()
public void ValidateIndex()
{
var header = new BlockHeader(
protocolVersion: 0,
index: -1,
difficulty: _fx.Next.Difficulty,
totalDifficulty: _fx.Next.TotalDifficulty,
Expand All @@ -92,6 +121,7 @@ public void ValidateDifficulty()
{
DateTimeOffset now = DateTimeOffset.UtcNow;
var genesisHeader = new BlockHeader(
protocolVersion: 0,
index: 0,
difficulty: 1000,
totalDifficulty: 1000,
Expand All @@ -109,6 +139,7 @@ public void ValidateDifficulty()
genesisHeader.Validate(DateTimeOffset.UtcNow));

var header1 = new BlockHeader(
protocolVersion: 0,
index: 10,
difficulty: 0,
totalDifficulty: 1000,
Expand All @@ -126,6 +157,7 @@ public void ValidateDifficulty()
header1.Validate(DateTimeOffset.UtcNow));

var header2 = new BlockHeader(
protocolVersion: 0,
index: 10,
difficulty: 1000,
totalDifficulty: 10,
Expand All @@ -148,6 +180,7 @@ public void ValidatePreviousHash()
{
DateTimeOffset now = DateTimeOffset.UtcNow;
var genesisHeader = new BlockHeader(
protocolVersion: 0,
index: 0,
difficulty: 0,
totalDifficulty: 0,
Expand All @@ -165,6 +198,7 @@ public void ValidatePreviousHash()
genesisHeader.Validate(DateTimeOffset.UtcNow));

var header = new BlockHeader(
protocolVersion: 0,
index: 10,
difficulty: 1000,
totalDifficulty: 1500,
Expand Down
21 changes: 21 additions & 0 deletions Libplanet/Blockchain/BlockChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1935,6 +1935,27 @@ HashDigest<SHA256> blockHash

private InvalidBlockException ValidateNextBlock(Block<T> nextBlock)
{
int actualProtocolVersion = nextBlock.ProtocolVersion;
const int currentProtocolVersion = Block<T>.CurrentProtocolVersion;
if (actualProtocolVersion > currentProtocolVersion)
{
string message =
$"The protocol version ({actualProtocolVersion}) of the block " +
$"#{nextBlock.Index} {nextBlock.Hash} is not supported by this node." +
$"The highest supported protocol version is {currentProtocolVersion}.";
throw new InvalidBlockProtocolVersionException(
actualProtocolVersion,
message
);
}
else if (Tip is { } tip && actualProtocolVersion < tip.ProtocolVersion)
{
string message =
"The protocol version is disallowed to be downgraded from the topmost block " +
$"in the chain ({actualProtocolVersion} < {tip.ProtocolVersion}).";
throw new InvalidBlockProtocolVersionException(actualProtocolVersion, message);
}

InvalidBlockException e = Policy.ValidateNextBlock(this, nextBlock);

if (!(e is null))
Expand Down
53 changes: 41 additions & 12 deletions Libplanet/Blocks/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace Libplanet.Blocks
public class Block<T>
where T : IAction, new()
{
public const int CurrentProtocolVersion = 0;

private int _bytesLength;

/// <summary>
Expand Down Expand Up @@ -51,6 +53,8 @@ public class Block<T>
/// Automatically determined if <c>null</c> is passed (which is default).</param>
/// <param name="stateRootHash">The <see cref="ITrie.Hash"/> of the states on the block.
/// </param>
/// <param name="protocolVersion">The protocol version. <see cref="CurrentProtocolVersion"/>
/// by default.</param>
/// <seealso cref="Mine"/>
public Block(
long index,
Expand All @@ -62,8 +66,10 @@ public Block(
DateTimeOffset timestamp,
IEnumerable<Transaction<T>> transactions,
HashDigest<SHA256>? preEvaluationHash = null,
HashDigest<SHA256>? stateRootHash = null)
HashDigest<SHA256>? stateRootHash = null,
int protocolVersion = CurrentProtocolVersion)
{
ProtocolVersion = protocolVersion;
Index = index;
Difficulty = difficulty;
TotalDifficulty = totalDifficulty;
Expand Down Expand Up @@ -145,33 +151,39 @@ public Block(

private Block(RawBlock rb)
: this(
#pragma warning disable SA1118
rb.Header.ProtocolVersion,
new HashDigest<SHA256>(rb.Header.Hash),
rb.Header.Index,
rb.Header.Difficulty,
rb.Header.TotalDifficulty,
new Nonce(rb.Header.Nonce.ToArray()),
rb.Header.Miner.Any() ? new Address(rb.Header.Miner) : (Address?)null,
#pragma warning disable MEN002 // Line is too long
rb.Header.PreviousHash.Any() ? new HashDigest<SHA256>(rb.Header.PreviousHash) : (HashDigest<SHA256>?)null,
#pragma warning restore MEN002 // Line is too long
rb.Header.PreviousHash.Any()
? new HashDigest<SHA256>(rb.Header.PreviousHash)
: (HashDigest<SHA256>?)null,
DateTimeOffset.ParseExact(
rb.Header.Timestamp,
BlockHeader.TimestampFormat,
CultureInfo.InvariantCulture).ToUniversalTime(),
#pragma warning disable MEN002 // Line is too long
rb.Header.TxHash.Any() ? new HashDigest<SHA256>(rb.Header.TxHash) : (HashDigest<SHA256>?)null,
#pragma warning restore MEN002 // Line is too long
rb.Header.TxHash.Any()
? new HashDigest<SHA256>(rb.Header.TxHash)
: (HashDigest<SHA256>?)null,
rb.Transactions
.Select(tx => Transaction<T>.Deserialize(tx.ToArray()))
.ToList(),
#pragma warning disable MEN002 // Line is too long
rb.Header.PreEvaluationHash.Any() ? new HashDigest<SHA256>(rb.Header.PreEvaluationHash) : (HashDigest<SHA256>?)null,
rb.Header.StateRootHash.Any() ? new HashDigest<SHA256>(rb.Header.StateRootHash) : (HashDigest<SHA256>?)null)
#pragma warning restore MEN002 // Line is too long
rb.Header.PreEvaluationHash.Any()
? new HashDigest<SHA256>(rb.Header.PreEvaluationHash)
: (HashDigest<SHA256>?)null,
rb.Header.StateRootHash.Any()
? new HashDigest<SHA256>(rb.Header.StateRootHash)
: (HashDigest<SHA256>?)null)
#pragma warning restore SA1118
{
}

private Block(
int protocolVersion,
HashDigest<SHA256> hash,
long index,
long difficulty,
Expand All @@ -186,6 +198,7 @@ private Block(
HashDigest<SHA256>? stateRootHash
)
{
ProtocolVersion = protocolVersion;
Index = index;
Difficulty = difficulty;
TotalDifficulty = totalDifficulty;
Expand All @@ -205,6 +218,12 @@ private Block(
Transactions = transactions.ToImmutableArray();
}

/// <summary>
/// The protocol version number.
/// </summary>
[IgnoreDuringEquals]
public int ProtocolVersion { get; }

/// <summary>
/// <see cref="Hash"/> is derived from a serialized <see cref="Block{T}"/>
/// after <see cref="Transaction{T}.Actions"/> are evaluated.
Expand Down Expand Up @@ -288,6 +307,7 @@ public BlockHeader Header

// FIXME: When hash is not assigned, should throw an exception.
return new BlockHeader(
protocolVersion: ProtocolVersion,
index: Index,
timestamp: timestampAsString,
nonce: Nonce.ToByteArray().ToImmutableArray(),
Expand Down Expand Up @@ -324,6 +344,7 @@ public BlockHeader Header
/// <param name="timestamp">The <see cref="DateTimeOffset"/> when mining started.</param>
/// <param name="transactions"><see cref="Transaction{T}"/>s that are going to be included
/// in the block.</param>
/// <param name="protocolVersion">The protocol version.</param>
/// <param name="cancellationToken">
/// A cancellation token used to propagate notification that this
/// operation should be canceled.</param>
Expand All @@ -336,6 +357,7 @@ public static Block<T> Mine(
HashDigest<SHA256>? previousHash,
DateTimeOffset timestamp,
IEnumerable<Transaction<T>> transactions,
int protocolVersion = CurrentProtocolVersion,
CancellationToken cancellationToken = default(CancellationToken))
{
var txs = transactions.OrderBy(tx => tx.Id).ToImmutableArray();
Expand All @@ -347,7 +369,8 @@ public static Block<T> Mine(
miner,
previousHash,
timestamp,
txs);
txs,
protocolVersion: protocolVersion);

// Poor man' way to optimize stamp...
// FIXME: We need to rather reorganize the serialization layout.
Expand Down Expand Up @@ -658,6 +681,12 @@ private byte[] SerializeForHash(HashDigest<SHA256>? stateRootHash = null)
.Add("difficulty", Difficulty)
.Add("nonce", Nonce.ToByteArray());

if (ProtocolVersion != 0)
{
// TODO: unit test
dict = dict.Add("protocol_version", ProtocolVersion);
}

if (!(Miner is null))
{
dict = dict.Add("reward_beneficiary", Miner.Value.ToByteArray());
Expand Down
Loading

0 comments on commit 4e7173c

Please sign in to comment.