Example #1
0
        public async Task IgnoreExistingBlocks()
        {
            var keyA = new PrivateKey();
            var keyB = new PrivateKey();

            Swarm <DumbAction> swarmA = CreateSwarm(keyA);
            Swarm <DumbAction> swarmB = CreateSwarm(keyB);

            BlockChain <DumbAction> chainA = swarmA.BlockChain;
            BlockChain <DumbAction> chainB = swarmB.BlockChain;

            Block <DumbAction> genesis = await chainA.MineBlock(keyA);

            chainB.Append(genesis);

            foreach (int i in Enumerable.Range(0, 3))
            {
                await chainA.MineBlock(keyA);
            }

            try
            {
                await StartAsync(swarmA);
                await StartAsync(swarmB);

                await BootstrapAsync(swarmB, swarmA.AsPeer);

                swarmA.BroadcastBlock(chainA[-1]);
                await swarmB.BlockAppended.WaitAsync();

                Assert.Equal(chainA.BlockHashes, chainB.BlockHashes);

                CancellationTokenSource cts = new CancellationTokenSource();
                swarmA.BroadcastBlock(chainA[-1]);
                Task t = swarmB.BlockAppended.WaitAsync(cts.Token);

                // Actually, previous code may pass this test if message is
                // delayed over 5 seconds.
                await Task.Delay(5000);

                Assert.False(t.IsCompleted);

                cts.Cancel();
            }
            finally
            {
                await StopAsync(swarmA);
                await StopAsync(swarmB);
            }
        }
Example #2
0
        public async Task MineBlockAsync(CancellationToken cancellationToken)
        {
            var txs = new HashSet <Transaction <PolymorphicAction <ActionBase> > >();

            var timeStamp     = DateTimeOffset.UtcNow;
            var prevTimeStamp = _chain?.Tip?.Timestamp;

            // FIXME 년도가 바뀌면 깨지는 계산 방식. 테스트 끝나면 변경해야함
            // 하루 한번 보상을 제공
            // TODO: Move ranking reward logic and condition into block action.
            if (prevTimeStamp is DateTimeOffset t && timeStamp.DayOfYear - t.DayOfYear == 1)
            {
                var rankingRewardTx = RankingReward();
                txs.Add(rankingRewardTx);
            }

            var invalidTxs = txs;

            try
            {
                var block = await _chain.MineBlock(Address, DateTimeOffset.UtcNow, cancellationToken : cancellationToken);

                if (_swarm.Running)
                {
                    _swarm.BroadcastBlock(block);
                }
            }
            catch (OperationCanceledException)
            {
                Log.Debug("Mining was canceled due to change of tip.");
            }
            catch (InvalidTxException invalidTxException)
            {
                var invalidTx = _chain.GetTransaction(invalidTxException.TxId);

                Log.Debug($"Tx[{invalidTxException.TxId}] is invalid. mark to unstage.");
                invalidTxs.Add(invalidTx);
            }
            catch (UnexpectedlyTerminatedActionException actionException)
            {
                if (actionException.TxId is TxId txId)
                {
                    Log.Debug(
                        $"Tx[{actionException.TxId}]'s action is invalid. mark to unstage. {actionException}");
                    invalidTxs.Add(_chain.GetTransaction(txId));
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, $"exception was thrown. {ex}");
            }
            finally
            {
                foreach (var invalidTx in invalidTxs)
                {
                    _chain.UnstageTransaction(invalidTx);
                }
            }
        }
Example #3
0
        public async Task BroadcastBlockToReconnectedPeer()
        {
            var miner = new PrivateKey();
            Swarm <DumbAction>      seed            = CreateSwarm(miner);
            BlockChain <DumbAction> chainWithBlocks = seed.BlockChain;

            var privateKey            = new PrivateKey();
            Swarm <DumbAction> swarmA = CreateSwarm(privateKey: privateKey);
            Swarm <DumbAction> swarmB = CreateSwarm(privateKey: privateKey);

            foreach (int i in Enumerable.Range(0, 10))
            {
                await chainWithBlocks.MineBlock(miner);
            }

            try
            {
                await StartAsync(seed);
                await StartAsync(swarmA);
                await StartAsync(swarmB);

                Assert.Equal(swarmA.AsPeer, swarmB.AsPeer);

                await swarmA.AddPeersAsync(new[] { seed.AsPeer }, null);
                await StopAsync(swarmA);

                await seed.PeerDiscovery.RefreshTableAsync(
                    TimeSpan.Zero,
                    default(CancellationToken));

                Assert.DoesNotContain(swarmA.AsPeer, seed.Peers);

                await swarmB.AddPeersAsync(new[] { seed.AsPeer }, null);

                // This is added for context switching.
                await Task.Delay(100);

                Assert.Contains(swarmB.AsPeer, seed.Peers);
                Assert.Contains(seed.AsPeer, swarmB.Peers);

                seed.BroadcastBlock(chainWithBlocks.Tip);

                await swarmB.BlockAppended.WaitAsync();

                Assert.NotEqual(chainWithBlocks.BlockHashes, swarmA.BlockChain.BlockHashes);
                Assert.Equal(chainWithBlocks.BlockHashes, swarmB.BlockChain.BlockHashes);
            }
            finally
            {
                await StopAsync(seed);
                await StopAsync(swarmA);
                await StopAsync(swarmB);

                seed.Dispose();
                swarmA.Dispose();
                swarmB.Dispose();
            }
        }
Example #4
0
        public async Task <Block <PolymorphicAction <ActionBase> > > MineBlockAsync(
            int maxTransactions,
            CancellationToken cancellationToken)
        {
            var txs = new HashSet <Transaction <PolymorphicAction <ActionBase> > >();

            var invalidTxs = txs;
            Block <PolymorphicAction <ActionBase> > block = null;

            try
            {
                block = await _chain.MineBlock(
                    Address,
                    DateTimeOffset.UtcNow,
                    cancellationToken : cancellationToken,
                    maxTransactions : maxTransactions);

                if (_swarm.Running)
                {
                    _swarm.BroadcastBlock(block);
                }
            }
            catch (OperationCanceledException)
            {
                Log.Debug("Mining was canceled due to change of tip.");
            }
            catch (InvalidTxException invalidTxException)
            {
                var invalidTx = _chain.GetTransaction(invalidTxException.TxId);

                Log.Debug($"Tx[{invalidTxException.TxId}] is invalid. mark to unstage.");
                invalidTxs.Add(invalidTx);
            }
            catch (UnexpectedlyTerminatedActionException actionException)
            {
                if (actionException.TxId is TxId txId)
                {
                    Log.Debug(
                        $"Tx[{actionException.TxId}]'s action is invalid. mark to unstage. {actionException}");
                    invalidTxs.Add(_chain.GetTransaction(txId));
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, $"exception was thrown. {ex}");
            }
            finally
            {
#pragma warning disable LAA1002
                foreach (var invalidTx in invalidTxs)
#pragma warning restore LAA1002
                {
                    _chain.UnstageTransaction(invalidTx);
                }
            }

            return(block);
        }
Example #5
0
        public async Task BroadcastBlockWithoutGenesis()
        {
            var keyA = new PrivateKey();
            var keyB = new PrivateKey();

            Swarm <DumbAction> swarmA = CreateSwarm(keyA);
            Swarm <DumbAction> swarmB = CreateSwarm(keyB);

            BlockChain <DumbAction> chainA = swarmA.BlockChain;
            BlockChain <DumbAction> chainB = swarmB.BlockChain;

            try
            {
                await StartAsync(swarmA);
                await StartAsync(swarmB);

                await BootstrapAsync(swarmB, swarmA.AsPeer);

                await chainA.MineBlock(keyA);

                swarmA.BroadcastBlock(chainA[-1]);

                await swarmB.BlockAppended.WaitAsync();

                Assert.Equal(chainB.BlockHashes, chainA.BlockHashes);

                await chainA.MineBlock(keyB);

                swarmA.BroadcastBlock(chainA[-1]);

                await swarmB.BlockAppended.WaitAsync();

                Assert.Equal(chainB.BlockHashes, chainA.BlockHashes);
            }
            finally
            {
                await StopAsync(swarmA);
                await StopAsync(swarmB);
            }
        }
Example #6
0
        public async Task BroadcastIgnoreFromDifferentGenesisHash()
        {
            var receiverKey = new PrivateKey();
            Swarm <DumbAction>      receiverSwarm = CreateSwarm(receiverKey);
            BlockChain <DumbAction> receiverChain = receiverSwarm.BlockChain;
            var seedStateStore = new TrieStateStore(new MemoryKeyValueStore());
            IBlockPolicy <DumbAction> policy            = receiverChain.Policy;
            Block <DumbAction>        mismatchedGenesis = new BlockContent <DumbAction>
            {
                PublicKey = receiverKey.PublicKey,
                Timestamp = DateTimeOffset.MinValue,
            }
            .Mine(policy.GetHashAlgorithm(0))
            .Evaluate(receiverKey, policy.BlockAction, seedStateStore);
            BlockChain <DumbAction> seedChain = TestUtils.MakeBlockChain(
                policy,
                new DefaultStore(path: null),
                seedStateStore,
                genesisBlock: mismatchedGenesis);
            var seedMiner = new PrivateKey();
            Swarm <DumbAction> seedSwarm = CreateSwarm(seedChain, seedMiner);

            try
            {
                await StartAsync(receiverSwarm);
                await StartAsync(seedSwarm);

                await receiverSwarm.AddPeersAsync(new[] { seedSwarm.AsPeer }, null);

                Block <DumbAction> block = await seedChain.MineBlock(seedMiner);

                seedSwarm.BroadcastBlock(block);
                while (!((NetMQTransport)receiverSwarm.Transport).MessageHistory
                       .Any(msg => msg is BlockHeaderMessage))
                {
                    await Task.Delay(100);
                }

                await Task.Delay(100);

                Assert.NotEqual(seedChain.Tip, receiverChain.Tip);
            }
            finally
            {
                await StopAsync(seedSwarm);
                await StopAsync(receiverSwarm);

                seedSwarm.Dispose();
            }
        }
Example #7
0
        public async Task BroadcastIgnoreFromDifferentGenesisHash()
        {
            Swarm <DumbAction>      receiverSwarm = CreateSwarm();
            BlockChain <DumbAction> receiverChain = receiverSwarm.BlockChain;
            var invalidGenesisBlock = new Block <DumbAction>(
                0,
                0,
                0,
                new Nonce(new byte[] { 0x10, 0x00, 0x00, 0x00 }),
                receiverSwarm.Address,
                null,
                DateTimeOffset.MinValue,
                Enumerable.Empty <Transaction <DumbAction> >());
            BlockChain <DumbAction> seedChain = TestUtils.MakeBlockChain(
                receiverChain.Policy,
                new DefaultStore(path: null),
                new TrieStateStore(new MemoryKeyValueStore(), new MemoryKeyValueStore()),
                genesisBlock: invalidGenesisBlock);
            Swarm <DumbAction> seedSwarm = CreateSwarm(seedChain);

            try
            {
                await StartAsync(receiverSwarm);
                await StartAsync(seedSwarm);

                await receiverSwarm.AddPeersAsync(new[] { seedSwarm.AsPeer }, null);

                Block <DumbAction> block = await seedChain.MineBlock(seedSwarm.Address);

                seedSwarm.BroadcastBlock(block);
                while (!((NetMQTransport)receiverSwarm.Transport).MessageHistory
                       .Any(msg => msg is BlockHeaderMessage))
                {
                    await Task.Delay(100);
                }

                await Task.Delay(100);

                Assert.NotEqual(seedChain.Tip, receiverChain.Tip);
            }
            finally
            {
                await StopAsync(seedSwarm);
                await StopAsync(receiverSwarm);

                seedSwarm.Dispose();
            }
        }
        public async Task DetermineCanonicalChain(short canonComparerType)
        {
            IComparer <IBlockExcerpt> canonComparer;

            switch (canonComparerType)
            {
            default:
                canonComparer = new TotalDifficultyComparer();
                break;

            case 1:
                canonComparer = new AnonymousComparer <IBlockExcerpt>((a, b) =>
                                                                      string.Compare(
                                                                          a.Hash.ToString(),
                                                                          b.Hash.ToString(),
                                                                          StringComparison.Ordinal
                                                                          )
                                                                      );
                break;
            }

            var policy = new BlockPolicy <DumbAction>(
                new MinerReward(1),
                canonicalChainComparer: canonComparer
                );
            BlockChain <DumbAction> chain1 = TestUtils.MakeBlockChain(
                policy,
                new DefaultStore(null),
                new TrieStateStore(new MemoryKeyValueStore())
                );
            BlockChain <DumbAction> chain2 = TestUtils.MakeBlockChain(
                policy,
                new DefaultStore(null),
                new TrieStateStore(new MemoryKeyValueStore())
                );

            var key1 = new PrivateKey();
            var key2 = new PrivateKey();

            Swarm <DumbAction> miner1 = CreateSwarm(chain1, key1);
            Swarm <DumbAction> miner2 = CreateSwarm(chain2, key2);

            await chain1.MineBlock(key1);

            await chain1.MineBlock(key2);

            Block <DumbAction> bestBlock;

            switch (canonComparerType)
            {
            default:
                long nextDifficulty =
                    (long)chain1.Tip.TotalDifficulty + policy.GetNextBlockDifficulty(chain2);
                bestBlock = TestUtils.MineNext(
                    chain2.Tip,
                    policy.GetHashAlgorithm,
                    difficulty: nextDifficulty,
                    blockInterval: TimeSpan.FromMilliseconds(1),
                    miner: TestUtils.ChainPrivateKey.PublicKey
                    ).Evaluate(TestUtils.ChainPrivateKey, chain2);
                _output.WriteLine("chain1's total difficulty: {0}", chain1.Tip.TotalDifficulty);
                _output.WriteLine("chain2's total difficulty: {0}", bestBlock.TotalDifficulty);
                break;

            case 1:
                string chain1TipHash = chain1.Tip.Hash.ToString();
                string hashStr;
                do
                {
                    bestBlock = TestUtils.MineNext(
                        chain2.Tip,
                        policy.GetHashAlgorithm,
                        difficulty: policy.GetNextBlockDifficulty(chain2),
                        blockInterval: TimeSpan.FromMilliseconds(1),
                        miner: TestUtils.ChainPrivateKey.PublicKey
                        ).Evaluate(TestUtils.ChainPrivateKey, chain2);
                    hashStr = bestBlock.Hash.ToString();
                    _output.WriteLine("chain1's tip hash: {0}", chain1.Tip.Hash);
                    _output.WriteLine("chain2's tip hash: {0}", bestBlock.Hash);
                    _output.WriteLine(string.Empty);
                }while (string.Compare(chain1TipHash, hashStr, StringComparison.Ordinal) >= 0);
                break;
            }

            Assert.True(
                canonComparer.Compare(
                    new BlockPerception(bestBlock),
                    chain1.PerceiveBlock(chain1.Tip)
                    ) > 0
                );
            chain2.Append(bestBlock);

            try
            {
                await StartAsync(miner1);
                await StartAsync(miner2);

                await BootstrapAsync(miner2, miner1.AsPeer);

                miner2.BroadcastBlock(bestBlock);
                _output.WriteLine("miner1 is waiting for a new block...");
                await miner1.BlockReceived.WaitAsync();

                Assert.Equal(miner1.BlockChain.Tip, bestBlock);
                Assert.Equal(miner2.BlockChain.Tip, bestBlock);
            }
            finally
            {
                await StopAsync(miner1);
                await StopAsync(miner2);

                miner1.Dispose();
                miner2.Dispose();
            }
        }
Example #9
0
        MineBlockAsync(CancellationToken cancellationToken)
        {
            var txs = new HashSet <Transaction <PolymorphicAction <ActionBase> > >();

            var invalidTxs = txs;
            Block <PolymorphicAction <ActionBase> > mainBlock = null;
            Block <PolymorphicAction <ActionBase> > subBlock  = null;

            try
            {
                mainBlock = await _mainChain.MineBlock(
                    _privateKey,
                    DateTimeOffset.UtcNow,
                    cancellationToken : cancellationToken);

                subBlock = await _subChain.MineBlock(
                    _privateKey,
                    DateTimeOffset.UtcNow,
                    cancellationToken : cancellationToken);

                if (_reorgInterval != 0 && subBlock.Index % _reorgInterval == 0)
                {
                    Log.Debug("Force reorg!");
                    _subSwarm.BroadcastBlock(subBlock);
                }

                if (_mainSwarm.Running)
                {
                    _mainSwarm.BroadcastBlock(mainBlock);
                }
            }
            catch (OperationCanceledException)
            {
                Log.Debug("Mining was canceled due to change of tip.");
            }
            catch (InvalidTxException invalidTxException)
            {
                var invalidTx = _mainChain.GetTransaction(invalidTxException.TxId);

                Log.Debug($"Tx[{invalidTxException.TxId}] is invalid. mark to unstage.");
                invalidTxs.Add(invalidTx);
            }
            catch (UnexpectedlyTerminatedActionException actionException)
            {
                if (actionException.TxId is TxId txId)
                {
                    Log.Debug(
                        $"Tx[{actionException.TxId}]'s action is invalid. mark to unstage. {actionException}");
                    invalidTxs.Add(_mainChain.GetTransaction(txId));
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, $"exception was thrown. {ex}");
            }
            finally
            {
#pragma warning disable LAA1002
                foreach (var invalidTx in invalidTxs)
#pragma warning restore LAA1002
                {
                    _mainChain.UnstageTransaction(invalidTx);
                    _subChain.UnstageTransaction(invalidTx);
                }
            }

            return(mainBlock, subBlock);
        }
Example #10
0
        private IEnumerator CoMiner()
        {
            while (true)
            {
                var txs = new HashSet <Transaction <PolymorphicAction <ActionBase> > >();

                var task = Task.Run(async() =>
                {
                    var block = await _blocks.MineBlock(Address);

                    if (_swarm?.Running ?? false)
                    {
                        _swarm.BroadcastBlock(block);
                    }

                    return(block);
                });
                yield return(new WaitUntil(() => task.IsCompleted));

                if (!task.IsCanceled && !task.IsFaulted)
                {
                    var block = task.Result;
                    Debug.Log($"created block index: {block.Index}, difficulty: {block.Difficulty}");
                }
                else
                {
                    var invalidTxs   = txs;
                    var retryActions = new HashSet <IImmutableList <PolymorphicAction <ActionBase> > >();

                    if (task.IsFaulted)
                    {
                        foreach (var ex in task.Exception.InnerExceptions)
                        {
                            if (ex is InvalidTxNonceException invalidTxNonceException)
                            {
                                var invalidNonceTx = _store.GetTransaction <PolymorphicAction <ActionBase> >(invalidTxNonceException.TxId);

                                if (invalidNonceTx.Signer == Address)
                                {
                                    Debug.Log($"Tx[{invalidTxNonceException.TxId}] nonce is invalid. Retry it.");
                                    retryActions.Add(invalidNonceTx.Actions);
                                }
                            }

                            if (ex is InvalidTxException invalidTxException)
                            {
                                Debug.Log($"Tx[{invalidTxException.TxId}] is invalid. mark to unstage.");
                                invalidTxs.Add(_store.GetTransaction <PolymorphicAction <ActionBase> >(invalidTxException.TxId));
                            }

                            Debug.LogException(ex);
                        }
                    }
                    _blocks.UnstageTransactions(invalidTxs);

                    foreach (var retryAction in retryActions)
                    {
                        MakeTransaction(retryAction, true);
                    }
                }
            }
        }
        public async Task BroadcastBlockToReconnectedPeer()
        {
            var miner      = new PrivateKey();
            var policy     = new NullBlockPolicy <DumbAction>();
            var fx         = new MemoryStoreFixture(policy.BlockAction);
            var minerChain = MakeBlockChain(policy, fx.Store, fx.StateStore);

            foreach (int i in Enumerable.Range(0, 10))
            {
                await minerChain.MineBlock(miner);
            }

            Swarm <DumbAction> seed = CreateSwarm(
                miner,
                policy: policy,
                genesis: minerChain.Genesis
                );
            BlockChain <DumbAction> seedChain = seed.BlockChain;

            var privateKey            = new PrivateKey();
            Swarm <DumbAction> swarmA = CreateSwarm(
                privateKey: privateKey,
                policy: policy,
                genesis: minerChain.Genesis
                );
            Swarm <DumbAction> swarmB = CreateSwarm(
                privateKey: privateKey,
                policy: policy,
                genesis: minerChain.Genesis
                );

            foreach (BlockHash blockHash in minerChain.BlockHashes.Skip(1).Take(4))
            {
                seedChain.Append(minerChain[blockHash]);
            }

            try
            {
                await StartAsync(seed);
                await StartAsync(swarmA);
                await StartAsync(swarmB);

                Assert.Equal(swarmA.AsPeer, swarmB.AsPeer);

                await swarmA.AddPeersAsync(new[] { seed.AsPeer }, null);
                await StopAsync(swarmA);

                await seed.PeerDiscovery.RefreshTableAsync(
                    TimeSpan.Zero,
                    default(CancellationToken));

                Assert.DoesNotContain(swarmA.AsPeer, seed.Peers);

                foreach (BlockHash blockHash in minerChain.BlockHashes.Skip(5))
                {
                    seedChain.Append(minerChain[blockHash]);
                }

                await swarmB.AddPeersAsync(new[] { seed.AsPeer }, null);

                // This is added for context switching.
                await Task.Delay(100);

                Assert.Contains(swarmB.AsPeer, seed.Peers);
                Assert.Contains(seed.AsPeer, swarmB.Peers);

                seed.BroadcastBlock(seedChain.Tip);

                await swarmB.BlockAppended.WaitAsync();

                Assert.NotEqual(seedChain.BlockHashes, swarmA.BlockChain.BlockHashes);
                Assert.Equal(seedChain.BlockHashes, swarmB.BlockChain.BlockHashes);
            }
            finally
            {
                await StopAsync(seed);
                await StopAsync(swarmA);
                await StopAsync(swarmB);

                seed.Dispose();
                swarmA.Dispose();
                swarmB.Dispose();
            }
        }