public void GetStatesReturnsEarlyForNonexistentAccount() { var tracker = new StoreTracker(_fx.Store); var chain = new BlockChain <DumbAction>( new NullPolicy <DumbAction>(), tracker ); Block <DumbAction> b = TestUtils.MineGenesis <DumbAction>(); chain.Append(b); for (int i = 0; i < 20; ++i) { b = TestUtils.MineNext(b); chain.Append(b); } tracker.ClearLogs(); Address nonexistent = new PrivateKey().PublicKey.ToAddress(); AddressStateMap result = chain.GetStates(new[] { nonexistent }); Assert.False(result.ContainsKey(nonexistent)); var callCount = tracker.Logs.Where( triple => triple.Item1.Equals("GetBlockStates") ).Select(triple => triple.Item2).Count(); Assert.True( callCount <= 1, $"GetBlocksStates() was called {callCount} times" ); }
public void ValidateWithMultipleAlgorithms() { using (var fx = new MemoryStoreFixture()) { IBlockPolicy <DumbAction> policy = new MultiAlgoPolicy <DumbAction>(); BlockChain <DumbAction> chain = TestUtils.MakeBlockChain( policy, fx.Store, fx.StateStore, new DumbAction[0] ); HashAlgorithmType invalidAlgo = HashAlgorithmType.Of <SHA1>(); Block <DumbAction> invalidBlock = TestUtils.MineNext( chain.Genesis, _ => invalidAlgo, miner: TestUtils.ChainPrivateKey.PublicKey, difficulty: policy.GetNextBlockDifficulty(chain) ).Evaluate(TestUtils.ChainPrivateKey, chain); Assert.Throws <InvalidBlockHashAlgorithmTypeException>( () => chain.Append(invalidBlock)); HashAlgorithmType validAlgo = HashAlgorithmType.Of <MD5>(); Block <DumbAction> validBlock = TestUtils.MineNext( chain.Genesis, _ => validAlgo, miner: TestUtils.ChainPrivateKey.PublicKey, difficulty: policy.GetNextBlockDifficulty(chain) ).Evaluate(TestUtils.ChainPrivateKey, chain); chain.Append(validBlock); } }
public void ForkBlockIndex(int branchPointIndex) { var store = Fx.Store; var blocks = new BlockChain <DumbAction>( new NullPolicy <DumbAction>(), store, Fx.GenesisBlock ); blocks.Append(Fx.Block1); blocks.Append(Fx.Block2); blocks.Append(Fx.Block3); var forked = new BlockChain <DumbAction>( new NullPolicy <DumbAction>(), store, Guid.NewGuid(), Fx.GenesisBlock ); store.ForkBlockIndexes(blocks.Id, forked.Id, blocks[branchPointIndex].Hash); Assert.Equal( store.IterateIndexes(blocks.Id, 0, branchPointIndex + 1).ToList(), store.IterateIndexes(forked.Id).ToList() ); Assert.Equal(branchPointIndex + 1, store.CountIndex(forked.Id)); }
private void ValidateNextBlockInvalidStateRootHash() { IKeyValueStore stateKeyValueStore = new MemoryKeyValueStore(), stateHashKeyValueStore = new MemoryKeyValueStore(); var policy = new BlockPolicy <DumbAction>( null, TimeSpan.FromHours(3), 1024, 128); var stateStore = new TrieStateStore(stateKeyValueStore, stateHashKeyValueStore); // FIXME: It assumes that _fx.GenesisBlock doesn't update any states with transactions. // Actually, it depends on BlockChain<T> to update states and it makes hard to // calculate state root hash. To resolve this problem, // it should be moved into StateStore. var genesisBlock = TestUtils.MineGenesis <DumbAction>( blockAction: policy.BlockAction, checkStateRootHash: true); var store = new DefaultStore(null); var chain = new BlockChain <DumbAction>( policy, store, stateStore, genesisBlock); var validNext = Block <DumbAction> .Mine( 1, 1024, genesisBlock.TotalDifficulty, genesisBlock.Miner.Value, genesisBlock.Hash, genesisBlock.Timestamp.AddSeconds(1), _emptyTransaction); chain.ExecuteActions(validNext); validNext = new Block <DumbAction>(validNext, stateStore.GetRootHash(validNext.Hash)); chain.Append(validNext); var invalidStateRootHash = Block <DumbAction> .Mine( 2, 1032, validNext.TotalDifficulty, genesisBlock.Miner.Value, validNext.Hash, validNext.Timestamp.AddSeconds(1), _emptyTransaction); var actionEvaluations = _blockChain.BlockEvaluator.EvaluateActions( invalidStateRootHash, StateCompleterSet <DumbAction> .Recalculate); chain.SetStates(invalidStateRootHash, actionEvaluations, false); invalidStateRootHash = new Block <DumbAction>( invalidStateRootHash, new HashDigest <SHA256>(TestUtils.GetRandomBytes(HashDigest <SHA256> .Size))); Assert.Throws <InvalidBlockStateRootHashException>(() => chain.Append(invalidStateRootHash)); }
public void GetStatesOnlyDrillsDownUntilRequestedAddressesAreFound() { var tracker = new StoreTracker(_fx.Store); var chain = new BlockChain <DumbAction>( new NullPolicy <DumbAction>(), tracker ); Block <DumbAction> b = TestUtils.MineGenesis <DumbAction>(); chain.Append(b); Address[] addresses = new Address[30]; for (int i = 0; i < addresses.Length; ++i) { var privateKey = new PrivateKey(); Address address = privateKey.PublicKey.ToAddress(); addresses[i] = address; DumbAction[] actions = { new DumbAction(address, "foo"), new DumbAction(i < 1 ? address : addresses[i - 1], "bar"), }; Transaction <DumbAction>[] txs = { Transaction <DumbAction> .Create(privateKey, actions), }; b = TestUtils.MineNext(b, txs); chain.Append(b); } tracker.ClearLogs(); int testingDepth = addresses.Length / 2; Address[] targetAddresses = Enumerable.Range( testingDepth, Math.Min(10, addresses.Length - testingDepth - 1) ).Select(i => addresses[i]).ToArray(); AddressStateMap result = chain.GetStates(targetAddresses); string resultString = string.Join(", ", result.Keys); string targetString = string.Join(", ", targetAddresses); string message = $"The result dictionary ({resultString}) does not " + $"cover all requested addresses ({targetString})."; foreach (Address targetAddress in targetAddresses) { Assert.True(result.ContainsKey(targetAddress), message); } var callCount = tracker.Logs.Where( triple => triple.Item1.Equals("GetBlockStates") ).Select(triple => triple.Item2).Count(); Assert.True(testingDepth >= callCount); }
public void ValidateNextBlockInvalidIndex() { _chain.Append(_validNext); var invalidIndexBlock = Block <DumbAction> .Mine( 1, 1, _genesis.Miner.Value, _validNext.Hash, _validNext.Timestamp.AddSeconds(1), _emptyTransaction); Assert.IsType <InvalidBlockIndexException>( _policy.ValidateNextBlock(_chain, invalidIndexBlock)); }
public async Task TransferNCGHistories() { PrivateKey minerPrivateKey = new PrivateKey(); Address sender = minerPrivateKey.ToAddress(), recipient = new PrivateKey().ToAddress(); await BlockChain.MineBlock(sender); await BlockChain.MineBlock(recipient); var currency = new GoldCurrencyState((Dictionary)BlockChain.GetState(Addresses.GoldCurrency)).Currency; var transferAsset = new TransferAsset(sender, recipient, new FungibleAssetValue(currency, 10, 0)); var tx = BlockChain.MakeTransaction(minerPrivateKey, new PolymorphicAction <ActionBase>[] { transferAsset }); var block = await BlockChain.MineBlock(minerPrivateKey.ToAddress(), append : false); BlockChain.Append(block); Assert.NotNull(StandaloneContextFx.Store?.GetTxExecution(block.Hash, tx.Id)); var blockHashHex = ByteUtil.Hex(block.Hash.ToByteArray()); var result = await ExecuteQueryAsync( $"{{ transferNCGHistories(blockHash: \"{blockHashHex}\") {{ blockHash txId sender recipient amount }} }}"); Assert.Null(result.Errors); Assert.Equal(new List <object> { new Dictionary <string, object> { ["blockHash"] = block.Hash.ToString(), ["txId"] = tx.Id.ToString(), ["sender"] = transferAsset.Sender.ToString(), ["recipient"] = transferAsset.Recipient.ToString(), ["amount"] = transferAsset.Amount.GetQuantityString(), } }, result.Data.As <Dictionary <string, object> >()["transferNCGHistories"]); }
public override BlockChain <DumbAction> TransferAssetInBlock() { BlockChain <DumbAction> chain = base.TransferAssetInBlock(); DumbAction action = new DumbAction(_addr[0], "a", _addr[0], _addr[0], 1); Transaction <DumbAction> tx = Transaction <DumbAction> .Create( chain.GetNextTxNonce(_addr[0]), _keys[0], chain.Genesis.Hash, new[] { action } ); chain.Append( TestUtils.MineNext( chain.Tip, new[] { tx }, protocolVersion: ProtocolVersion ).AttachStateRootHash(chain.StateStore, chain.Policy.BlockAction) ); Assert.Equal( DumbAction.DumbCurrency * 6, chain.GetBalance(_addr[0], DumbAction.DumbCurrency) ); return(chain); }
public override BlockChain <DumbAction> TransferAssetInBlock() { BlockChain <DumbAction> chain = base.TransferAssetInBlock(); DumbAction action = new DumbAction(_addr[0], "a", _addr[0], _addr[0], 1); Transaction <DumbAction> tx = Transaction <DumbAction> .Create( chain.GetNextTxNonce(_addr[0]), _keys[0], chain.Genesis.Hash, new[] { action } ); chain.Append( TestUtils.MineNext( chain.Tip, chain.Policy.GetHashAlgorithm, new[] { tx }, miner: _keys[1].PublicKey, protocolVersion: ProtocolVersion ).Evaluate(_keys[1], chain) ); Assert.Equal( DumbAction.DumbCurrency * 5, chain.GetBalance(_addr[0], DumbAction.DumbCurrency) ); return(chain); }
public void Copy() { using (StoreFixture fx = FxConstructor()) using (StoreFixture fx2 = FxConstructor()) { IStore s1 = fx.Store, s2 = fx2.Store; var blocks = new BlockChain <DumbAction>( new NullPolicy <DumbAction>(), s1, Fx.GenesisBlock ); // FIXME: Need to add more complex blocks/transactions. blocks.Append(Fx.Block1); blocks.Append(Fx.Block2); blocks.Append(Fx.Block3); s1.Copy(to: Fx.Store); Fx.Store.Copy(to: s2); Assert.Equal(s1.ListChainIds().ToHashSet(), s2.ListChainIds().ToHashSet()); Assert.Equal(s1.GetCanonicalChainId(), s2.GetCanonicalChainId()); foreach (Guid chainId in s1.ListChainIds()) { Assert.Equal(s1.IterateIndexes(chainId), s2.IterateIndexes(chainId)); foreach (HashDigest <SHA256> blockHash in s1.IterateIndexes(chainId)) { Assert.Equal( s1.GetBlock <DumbAction>(blockHash), s2.GetBlock <DumbAction>(blockHash) ); Assert.Equal( s1.GetBlockStates(blockHash), s2.GetBlockStates(blockHash) ); } Assert.Equal( s1.ListAllStateReferences(chainId), s2.ListAllStateReferences(chainId) ); } // ArgumentException is thrown if the destination store is not empty. Assert.Throws <ArgumentException>(() => Fx.Store.Copy(fx2.Store)); } }
public void ValidateNextBlock() { _policy.ValidateNextBlock(_chain, _genesis); _chain.Append(_genesis); var validNextBlock = Block <DumbAction> .Mine( 1, 1, _genesis.Miner.Value, _genesis.Hash, _genesis.Timestamp.AddDays(1), _emptyTransaction); _policy.ValidateNextBlock(_chain, validNextBlock); }
public void AppendValidatesBlock() { var blockChain = new BlockChain <DumbAction>( new NullPolicy <DumbAction>( new InvalidBlockDifficultyException(string.Empty)), _fx.Store); Assert.Throws <InvalidBlockDifficultyException>( () => blockChain.Append(_fx.Block1)); }
public void ForkBlockIndex(int branchPointIndex) { var store = Fx.Store; var blocks = new BlockChain <DumbAction>( new NullPolicy <DumbAction>(), new VolatileStagePolicy <DumbAction>(), store, Fx.StateStore, Fx.GenesisBlock ); blocks.Append(Fx.Block1); blocks.Append(Fx.Block2); blocks.Append(Fx.Block3); var forked = new BlockChain <DumbAction>( new NullPolicy <DumbAction>(), new VolatileStagePolicy <DumbAction>(), store, Fx.StateStore, Guid.NewGuid(), Fx.GenesisBlock, renderers: null ); Logger.CompareBothChains( LogEventLevel.Debug, nameof(blocks), blocks, nameof(forked), forked ); store.ForkBlockIndexes(blocks.Id, forked.Id, blocks[branchPointIndex].Hash); Assert.Equal( store.IterateIndexes(blocks.Id, 0, branchPointIndex + 1).ToList(), store.IterateIndexes(forked.Id).ToList() ); Assert.Equal(branchPointIndex + 1, store.CountIndex(forked.Id)); }
public void Evaluate() { Address address = _contents.Tx0InBlock1.Signer; var blockAction = new SetStatesAtBlock(address, (Bencodex.Types.Integer) 123, 0); var policy = new BlockPolicy <Arithmetic>( blockAction: blockAction, blockInterval: TimeSpan.FromMilliseconds(3 * 60 * 60 * 1000), minimumDifficulty: 2, difficultyStability: 1 ); var stagePolicy = new VolatileStagePolicy <Arithmetic>(); PreEvaluationBlock <Arithmetic> preEvalGenesis = _contents.Genesis.Mine(policy.GetHashAlgorithm(0)); using (var fx = new DefaultStoreFixture()) { Block <Arithmetic> genesis = preEvalGenesis.Evaluate(_contents.GenesisKey, blockAction, fx.StateStore); AssertPreEvaluationBlocksEqual(preEvalGenesis, genesis); _output.WriteLine("#1: {0}", genesis); var blockChain = new BlockChain <Arithmetic>( policy, stagePolicy, fx.Store, fx.StateStore, genesis ); AssertBencodexEqual((Bencodex.Types.Integer) 123, blockChain.GetState(address)); HashDigest <SHA256> identicalGenesisStateRootHash = preEvalGenesis.DetermineStateRootHash(blockChain); AssertBytesEqual(genesis.StateRootHash, identicalGenesisStateRootHash); BlockContent <Arithmetic> content1 = _contents.Block1; content1.PreviousHash = genesis.Hash; content1.Difficulty = 2; content1.Transactions = new[] { _contents.Tx0InBlock1 }; PreEvaluationBlock <Arithmetic> preEval1 = content1.Mine(policy.GetHashAlgorithm(1)); Block <Arithmetic> block1 = preEval1.Evaluate(_contents.Block1Key, blockChain); AssertPreEvaluationBlocksEqual(preEval1, block1); _output.WriteLine("#1: {0}", block1); HashDigest <SHA256> identicalBlock1StateRootHash = preEval1.DetermineStateRootHash(blockChain); AssertBytesEqual(block1.StateRootHash, identicalBlock1StateRootHash); blockChain.Append(block1); AssertBencodexEqual((Bencodex.Types.Integer) 158, blockChain.GetState(address)); } }
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); } }
public void ForkStateReferencesChainIdNotFound() { var targetChainId = Guid.NewGuid(); Address address = Fx.Address1; Assert.Throws <ChainIdNotFoundException>(() => Fx.Store.ForkStateReferences(Fx.StoreChainId, targetChainId, Fx.Block1) ); var chain = new BlockChain <DumbAction>(new NullPolicy <DumbAction>(), Fx.Store); chain.Append(Fx.Block1); // Even if state references in a chain are empty it should not throw // ChainIdNotFoundException unless the chain in itself does not exist. Fx.Store.ForkStateReferences(chain.Id, targetChainId, Fx.Block1); }
public void ListAllStateReferences(StoreFixture fx) { Address address1 = fx.Address1; Address address2 = fx.Address2; Address address3 = new PrivateKey().PublicKey.ToAddress(); Transaction <DumbAction> tx4 = fx.MakeTransaction( new[] { new DumbAction(address1, "foo1"), new DumbAction(address2, "foo2"), } ); Block <DumbAction> block4 = TestUtils.MineNext(fx.Block3, new[] { tx4 }); Transaction <DumbAction> tx5 = fx.MakeTransaction( new[] { new DumbAction(address1, "bar1"), new DumbAction(address3, "bar3"), } ); Block <DumbAction> block5 = TestUtils.MineNext(block4, new[] { tx5 }); Block <DumbAction> block6 = TestUtils.MineNext(block5, new Transaction <DumbAction> [0]); var chain = new BlockChain <DumbAction>(new NullPolicy <DumbAction>(), fx.Store); chain.Append(fx.Block1); chain.Append(fx.Block2); chain.Append(fx.Block3); chain.Append(block4); chain.Append(block5); chain.Append(block6); IImmutableDictionary <Address, IImmutableList <HashDigest <SHA256> > > refs = fx.Store.ListAllStateReferences(chain.Id.ToString()); Assert.Equal( new HashSet <Address> { address1, address2, address3 }, refs.Keys.ToHashSet() ); Assert.Equal(new[] { block4.Hash, block5.Hash }, refs[address1]); Assert.Equal(new[] { block4.Hash }, refs[address2]); Assert.Equal(new[] { block5.Hash }, refs[address3]); }
public async Task GetDemandBlockHashes() { Swarm <DumbAction> minerSwarm = CreateSwarm(); Swarm <DumbAction> receiverSwarm = CreateSwarm(); Log.Logger.Information("Miner: {0}", minerSwarm.Address); Log.Logger.Information("Receiver: {0}", receiverSwarm.Address); BlockChain <DumbAction> minerChain = minerSwarm.BlockChain; BlockChain <DumbAction> receiverChain = receiverSwarm.BlockChain; (_, IEnumerable <Block <DumbAction> > blocks) = await MakeFixtureBlocksForPreloadAsyncCancellationTest(); foreach (Block <DumbAction> block in blocks) { minerChain.Append(block); } minerSwarm.FindNextHashesChunkSize = 2; await StartAsync(minerSwarm); (BoundPeer, long)[] peers =
public async Task <Block <NCAction> > MineBlockAsync( CancellationToken cancellationToken) { var txs = new HashSet <Transaction <NCAction> >(); var invalidTxs = txs; Block <NCAction> block = null; try { if (AuthorizedMiner) { _chain .GetStagedTransactionIds() .Select(txid => _chain.GetTransaction(txid)).ToList() .ForEach(tx => _chain.UnstageTransaction(tx)); } IEnumerable <Transaction <NCAction> > bannedTxs = _chain.GetStagedTransactionIds() .Select(txId => _chain.GetTransaction(txId)) .Where(tx => _bannedAccounts.Contains(tx.Signer)); foreach (Transaction <NCAction> tx in bannedTxs) { _chain.UnstageTransaction(tx); } // All miner needs proof in permissioned mining: Transaction <NCAction> proof = StageProofTransaction(); // Proof txs have priority over other txs: IComparer <Transaction <NCAction> > txPriority = GetProofTxPriority(proof); block = await _chain.MineBlock( _privateKey, DateTimeOffset.UtcNow, cancellationToken : cancellationToken, append : false, txPriority : txPriority); _chain.Append(block); if (_swarm is Swarm <NCAction> s && s.Running) { s.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); }
public void ValidateNextBlockWithManyTransactionsPerSigner() { var adminPrivateKey = new PrivateKey(); var adminPublicKey = adminPrivateKey.PublicKey; var blockPolicySource = new BlockPolicySource(Logger.None); IBlockPolicy <PolymorphicAction <ActionBase> > policy = blockPolicySource.GetPolicy( minimumDifficulty: 10_000, hashAlgorithmTypePolicy: null, maxBlockBytesPolicy: null, minTransactionsPerBlockPolicy: null, maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy .Default .Add(new SpannedSubPolicy <int>(0, null, null, 10)), maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy .Default .Add(new SpannedSubPolicy <int>(2, null, null, 5)), authorizedMinersPolicy: null, permissionedMinersPolicy: null); IStagePolicy <PolymorphicAction <ActionBase> > stagePolicy = new VolatileStagePolicy <PolymorphicAction <ActionBase> >(); Block <PolymorphicAction <ActionBase> > genesis = MakeGenesisBlock(adminPublicKey.ToAddress(), ImmutableHashSet <Address> .Empty); using var store = new DefaultStore(null); var stateStore = new TrieStateStore(new MemoryKeyValueStore()); var blockChain = new BlockChain <PolymorphicAction <ActionBase> >( policy, stagePolicy, store, stateStore, genesis ); int nonce = 0; List <Transaction <PolymorphicAction <ActionBase> > > GenerateTransactions(int count) { var list = new List <Transaction <PolymorphicAction <ActionBase> > >(); for (int i = 0; i < count; i++) { list.Add(Transaction <PolymorphicAction <ActionBase> > .Create( nonce++, adminPrivateKey, genesis.Hash, new PolymorphicAction <ActionBase>[] { } )); } return(list); } Assert.Equal(1, blockChain.Count); Block <PolymorphicAction <ActionBase> > block1 = new BlockContent <PolymorphicAction <ActionBase> > { Index = 1, Difficulty = policy.GetNextBlockDifficulty(blockChain), TotalDifficulty = blockChain.Tip.Difficulty + policy.GetNextBlockDifficulty(blockChain), PublicKey = adminPublicKey, PreviousHash = blockChain.Tip.Hash, Timestamp = DateTimeOffset.MinValue, Transactions = GenerateTransactions(10), }.Mine(policy.GetHashAlgorithm(1)).Evaluate(adminPrivateKey, blockChain); // Should be fine since policy hasn't kicked in yet. blockChain.Append(block1); Assert.Equal(2, blockChain.Count); Assert.True(blockChain.ContainsBlock(block1.Hash)); Block <PolymorphicAction <ActionBase> > block2 = new BlockContent <PolymorphicAction <ActionBase> > { Index = 2, Difficulty = policy.GetNextBlockDifficulty(blockChain), TotalDifficulty = blockChain.Tip.Difficulty + policy.GetNextBlockDifficulty(blockChain), PublicKey = adminPublicKey, PreviousHash = blockChain.Tip.Hash, Timestamp = DateTimeOffset.MinValue, Transactions = GenerateTransactions(10), }.Mine(policy.GetHashAlgorithm(2)).Evaluate(adminPrivateKey, blockChain); // Subpolicy kicks in. Assert.Throws <InvalidBlockTxCountPerSignerException>(() => blockChain.Append(block2)); Assert.Equal(2, blockChain.Count); Assert.False(blockChain.ContainsBlock(block2.Hash)); // Since failed, roll back nonce. nonce -= 10; // Limit should also pass. Block <PolymorphicAction <ActionBase> > block3 = new BlockContent <PolymorphicAction <ActionBase> > { Index = 2, Difficulty = policy.GetNextBlockDifficulty(blockChain), TotalDifficulty = blockChain.Tip.Difficulty + policy.GetNextBlockDifficulty(blockChain), PublicKey = adminPublicKey, PreviousHash = blockChain.Tip.Hash, Timestamp = DateTimeOffset.MinValue, Transactions = GenerateTransactions(5), }.Mine(policy.GetHashAlgorithm(2)).Evaluate(adminPrivateKey, blockChain); blockChain.Append(block3); Assert.Equal(3, blockChain.Count); Assert.True(blockChain.ContainsBlock(block3.Hash)); }
public async Task Preload() { Swarm <DumbAction> minerSwarm = _swarms[0]; Swarm <DumbAction> receiverSwarm = _swarms[1]; BlockChain <DumbAction> minerChain = _blockchains[0]; BlockChain <DumbAction> receiverChain = _blockchains[1]; var blocks = new List <Block <DumbAction> >(); foreach (int i in Enumerable.Range(0, 11)) { blocks.Add(TestUtils.MineNext( previousBlock: i == 0 ? minerChain.Genesis : blocks[i - 1], difficulty: 1024)); if (i != 10) { minerChain.Append(blocks[i]); } } var actualStates = new List <PreloadState>(); var progress = new Progress <PreloadState>(state => { _logger.Information("Received a progress event: {@State}", state); lock (actualStates) { actualStates.Add(state); if (actualStates.Count == 9) { minerChain.Append(blocks[10]); } } }); try { await StartAsync(minerSwarm); await receiverSwarm.AddPeersAsync(new[] { minerSwarm.AsPeer }, null); _logger.Verbose("Both chains before synchronization:"); _logger.CompareBothChains( LogEventLevel.Verbose, "Miner chain", minerChain, "Receiver chain", receiverChain ); minerSwarm.FindNextHashesChunkSize = 2; await receiverSwarm.PreloadAsync(TimeSpan.FromSeconds(15), progress); // Await 1 second to make sure all progresses is reported. await Task.Delay(1000); _logger.Verbose( $"Both chains after synchronization ({nameof(receiverSwarm.PreloadAsync)}):" ); _logger.CompareBothChains( LogEventLevel.Verbose, "Miner chain", minerChain, "Receiver chain", receiverChain ); Assert.Equal(minerChain.BlockHashes, receiverChain.BlockHashes); var expectedStates = new List <PreloadState>(); for (var i = 1; i < minerChain.Count; i++) { var b = minerChain[i]; var state = new BlockHashDownloadState { EstimatedTotalBlockHashCount = 10, ReceivedBlockHashCount = 1, SourcePeer = minerSwarm.AsPeer as BoundPeer, }; expectedStates.Add(state); } for (var i = 1; i < minerChain.Count; i++) { var b = minerChain[i]; var state = new BlockDownloadState { ReceivedBlockHash = b.Hash, TotalBlockCount = i == 9 || i == 10 ? 11 : 10, ReceivedBlockCount = i, SourcePeer = minerSwarm.AsPeer as BoundPeer, }; expectedStates.Add(state); } for (var i = 1; i < minerChain.Count; i++) { var b = minerChain[i]; var state = new BlockVerificationState { VerifiedBlockHash = b.Hash, TotalBlockCount = i == 9 || i == 10 ? 11 : 10, VerifiedBlockCount = i, }; expectedStates.Add(state); } for (var i = 1; i < minerChain.Count; i++) { var b = minerChain[i]; var state = new ActionExecutionState { ExecutedBlockHash = b.Hash, TotalBlockCount = 11, ExecutedBlockCount = i, }; expectedStates.Add(state); } _logger.Debug("Expected preload states: {@expectedStates}", expectedStates); _logger.Debug("Actual preload states: {@actualStates}", actualStates); Assert.Equal(expectedStates.Count, actualStates.Count); foreach (var states in expectedStates.Zip(actualStates, ValueTuple.Create)) { Assert.Equal(states.Item1, states.Item2); } } finally { await StopAsync(minerSwarm); await StopAsync(receiverSwarm); } }
public void ForkStateReferences() { Address addr1 = new PrivateKey().PublicKey.ToAddress(); Address addr2 = new PrivateKey().PublicKey.ToAddress(); Transaction <DumbAction>[] txsA = { _fx.MakeTransaction(new[] { new DumbAction(addr1, "foo"), }), }; Transaction <DumbAction>[] txsB = { _fx.MakeTransaction(new[] { new DumbAction(addr2, "bar"), }), }; Block <DumbAction> genesis = TestUtils.MineGenesis <DumbAction>(); _blockChain.Append(genesis); Block <DumbAction> b1 = TestUtils.MineNext( genesis, txsA, null, _blockChain.Policy.GetNextBlockDifficulty(_blockChain)); _blockChain.Append(b1); Block <DumbAction> b2 = TestUtils.MineNext( b1, txsB, null, _blockChain.Policy.GetNextBlockDifficulty(_blockChain)); _blockChain.Append(b2); Block <DumbAction> b3 = TestUtils.MineNext( b2, txsB, null, _blockChain.Policy.GetNextBlockDifficulty(_blockChain)); _blockChain.Append(b3); // Fork from genesis and append two empty blocks. BlockChain <DumbAction> forked = _blockChain.Fork(genesis.Hash); string fId = forked.Id.ToString(); Block <DumbAction> fb1 = TestUtils.MineNext(genesis, difficulty: b1.Difficulty); Block <DumbAction> fb2 = TestUtils.MineNext(fb1, difficulty: b2.Difficulty); forked.Append(fb1); forked.Append(fb2); Assert.Null( forked.Store.LookupStateReference(fId, addr1, forked.Tip)); Assert.Null( forked.Store.LookupStateReference(fId, addr2, forked.Tip)); // Fork from b1 and append a empty block. forked = _blockChain.Fork(b1.Hash); fId = forked.Id.ToString(); fb2 = TestUtils.MineNext(b1, difficulty: b2.Difficulty); forked.Append(fb2); Assert.Equal( b1.Hash, forked.Store.LookupStateReference(fId, addr1, forked.Tip)); Assert.Null( forked.Store.LookupStateReference(fId, addr2, forked.Tip)); // Fork from b2. forked = _blockChain.Fork(b2.Hash); fId = forked.Id.ToString(); Assert.Equal( b1.Hash, forked.Store.LookupStateReference(fId, addr1, forked.Tip)); Assert.Equal( b2.Hash, forked.Store.LookupStateReference(fId, addr2, forked.Tip)); }
public void Swap() { // FIXME: Poor man's fixture --- should make a proper fixture. Append(); BlockChain <DumbAction> fork = _blockChain.Fork(_blockChain.Tip.Hash); Address[] addresses = Enumerable.Repeat(0, 4) .Select(_ => new PrivateKey().PublicKey.ToAddress()) .ToArray(); Transaction <DumbAction>[][] txsA = { new[] { _fx.MakeTransaction(new[] { new DumbAction(addresses[0], "foo"), }), _fx.MakeTransaction(new[] { new DumbAction(addresses[1], "bar"), }), }, new[] { _fx.MakeTransaction(new[] { new DumbAction(addresses[2], "baz"), }), _fx.MakeTransaction(new[] { new DumbAction(addresses[3], "qux"), }), }, }; foreach (Transaction <DumbAction>[] txs in txsA) { Block <DumbAction> b = TestUtils.MineNext( _blockChain.Tip, txs, null, _blockChain.Policy.GetNextBlockDifficulty(_blockChain) ); _blockChain.Append(b); } Transaction <DumbAction>[] txsB = { _fx.MakeTransaction(new[] { new DumbAction(addresses[0], "fork-foo"), }), _fx.MakeTransaction(new[] { new DumbAction(addresses[1], "fork-bar"), new DumbAction(addresses[2], "fork-baz"), }), }; try { DumbAction.RenderRecords.Value = ImmutableList <DumbAction.RenderRecord> .Empty; Block <DumbAction> forkTip = TestUtils.MineNext( fork.Tip, txsB, null, _blockChain.Policy.GetNextBlockDifficulty(_blockChain) ); fork.Append(forkTip, DateTimeOffset.UtcNow, render: false); _blockChain.Swap(fork); var renders = DumbAction.RenderRecords.Value; int actionsCountA = txsA.Sum( a => a.Sum(tx => tx.Actions.Count) ); int actionsCountB = txsB.Sum(tx => tx.Actions.Count); Assert.Equal(actionsCountB + actionsCountA, renders.Count); Assert.True(renders.Take(actionsCountA).All(r => r.Unrender)); Assert.True(renders.Skip(actionsCountA).All(r => r.Render)); Assert.Equal("qux", renders[0].Action.Item); Assert.Equal("baz", renders[1].Action.Item); Assert.Equal("bar", renders[2].Action.Item); Assert.Equal("foo", renders[3].Action.Item); Assert.Equal("fork-foo", renders[4].Action.Item); Assert.Equal("fork-bar", renders[5].Action.Item); Assert.Equal("fork-baz", renders[6].Action.Item); } finally { DumbAction.RenderRecords.Value = ImmutableList <DumbAction.RenderRecord> .Empty; } }
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(); } }
public async Task <Block <PolymorphicAction <ActionBase> > > MineBlockAsync( int maxTransactions, CancellationToken cancellationToken) { var txs = new HashSet <Transaction <PolymorphicAction <ActionBase> > >(); var invalidTxs = txs; Transaction <PolymorphicAction <ActionBase> > authProof = null; Block <PolymorphicAction <ActionBase> > block = null; try { if (AuthorizedMiner) { authProof = StageProofTransaction(); } block = await _chain.MineBlock( Address, DateTimeOffset.UtcNow, cancellationToken : cancellationToken, maxTransactions : maxTransactions, append : false); if (authProof is Transaction <PolymorphicAction <ActionBase> > proof && !block.Transactions.Contains(proof)) { // For any reason, if the proof tx is not contained mine a new block again // without any transactions except for the proof tx. block = Block <PolymorphicAction <ActionBase> > .Mine( block.Index, block.Difficulty, block.TotalDifficulty - block.Difficulty, Address, block.PreviousHash, DateTimeOffset.UtcNow, new[] { proof }, block.ProtocolVersion, cancellationToken ); } _chain.Append(block); if (_swarm is Swarm <PolymorphicAction <ActionBase> > s && s.Running) { s.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); }
public async Task GetMultipleBlocksAtOnce() { var privateKey = new PrivateKey(); BlockChain <DumbAction> chainA = _blockchains[0]; BlockChain <DumbAction> chainB = _blockchains[1]; Swarm <DumbAction> swarmA = _swarms[0]; Swarm <DumbAction> swarmB = new Swarm <DumbAction>( chainB, privateKey, 1, host: IPAddress.Loopback.ToString()); Block <DumbAction> genesis = chainA.MineBlock(_fx1.Address1); chainB.Append(genesis); // chainA and chainB shares genesis block. chainA.MineBlock(_fx1.Address1); chainA.MineBlock(_fx1.Address1); try { await StartAsync(swarmA); await StartAsync(swarmB); var peer = swarmA.AsPeer; await swarmB.AddPeersAsync(new[] { peer }); IEnumerable <HashDigest <SHA256> > hashes = await swarmB.GetBlockHashesAsync( peer, new BlockLocator(new[] { genesis.Hash }), null); var netMQAddress = $"tcp://{peer.EndPoint.Host}:{peer.EndPoint.Port}"; using (var socket = new DealerSocket(netMQAddress)) { var request = new GetBlocks(hashes, 2); await socket.SendMultipartMessageAsync( request.ToNetMQMessage(privateKey)); NetMQMessage response = await socket.ReceiveMultipartMessageAsync(); Message parsedMessage = Message.Parse(response, true); Libplanet.Net.Messages.Blocks blockMessage = (Libplanet.Net.Messages.Blocks)parsedMessage; Assert.Equal(2, blockMessage.Payloads.Count); response = await socket.ReceiveMultipartMessageAsync(); parsedMessage = Message.Parse(response, true); blockMessage = (Libplanet.Net.Messages.Blocks)parsedMessage; Assert.Single(blockMessage.Payloads); } } finally { await Task.WhenAll( swarmA.StopAsync(), swarmB.StopAsync()); } }
public void ValidateNextBlockWithManyTransactions() { var adminPrivateKey = new PrivateKey(); var adminAddress = new Address(adminPrivateKey.PublicKey); var blockPolicySource = new BlockPolicySource(Logger.None); IBlockPolicy<PolymorphicAction<ActionBase>> policy = blockPolicySource.GetPolicy(3000, 10); Block<PolymorphicAction<ActionBase>> genesis = MakeGenesisBlock(adminAddress, ImmutableHashSet<Address>.Empty); using var store = new DefaultStore(null); var stateStore = new NoOpStateStore(); var blockChain = new BlockChain<PolymorphicAction<ActionBase>>( policy, store, stateStore, genesis ); int nonce = 0; List<Transaction<PolymorphicAction<ActionBase>>> GenerateTransactions(int count) { var list = new List<Transaction<PolymorphicAction<ActionBase>>>(); for (int i = 0; i < count; i++) { list.Add(Transaction<PolymorphicAction<ActionBase>>.Create( nonce++, adminPrivateKey, genesis.Hash, new PolymorphicAction<ActionBase>[] { } )); } return list; } Assert.Equal(1, blockChain.Count); Block<PolymorphicAction<ActionBase>> block1 = Block<PolymorphicAction<ActionBase>>.Mine( index: 1, difficulty: policy.GetNextBlockDifficulty(blockChain), previousTotalDifficulty: blockChain.Tip.TotalDifficulty, miner: adminAddress, previousHash: blockChain.Tip.Hash, timestamp: DateTimeOffset.MinValue, transactions: GenerateTransactions(5)); blockChain.Append(block1); Assert.Equal(2, blockChain.Count); Assert.True(blockChain.ContainsBlock(block1.Hash)); Block<PolymorphicAction<ActionBase>> block2 = Block<PolymorphicAction<ActionBase>>.Mine( index: 2, difficulty: policy.GetNextBlockDifficulty(blockChain), previousTotalDifficulty: blockChain.Tip.TotalDifficulty, miner: adminAddress, previousHash: blockChain.Tip.Hash, timestamp: DateTimeOffset.MinValue, transactions: GenerateTransactions(10)); blockChain.Append(block2); Assert.Equal(3, blockChain.Count); Assert.True(blockChain.ContainsBlock(block2.Hash)); Block<PolymorphicAction<ActionBase>> block3 = Block<PolymorphicAction<ActionBase>>.Mine( index: 3, difficulty: policy.GetNextBlockDifficulty(blockChain), previousTotalDifficulty: blockChain.Tip.TotalDifficulty, miner: adminAddress, previousHash: blockChain.Tip.Hash, timestamp: DateTimeOffset.MinValue, transactions: GenerateTransactions(11)); Assert.Throws<BlockExceedingTransactionsException>(() => blockChain.Append(block3)); Assert.Equal(3, blockChain.Count); Assert.False(blockChain.ContainsBlock(block3.Hash)); }
public async Task CanGetBlock() { Swarm swarmA = _swarms[0]; Swarm swarmB = _swarms[1]; BlockChain <BaseAction> chainA = _blockchains[0]; BlockChain <BaseAction> chainB = _blockchains[1]; Block <BaseAction> genesis = chainA.MineBlock(_fx1.Address1); chainB.Append(genesis); // chainA and chainB shares genesis block. Block <BaseAction> block1 = chainA.MineBlock(_fx1.Address1); Block <BaseAction> block2 = chainA.MineBlock(_fx1.Address1); try { await StartAsync(swarmA, chainA); await StartAsync(swarmB, chainA); await Assert.ThrowsAsync <PeerNotFoundException>( async() => await swarmB.GetBlockHashesAsync( swarmA.AsPeer, new BlockLocator(new[] { genesis.Hash }), null)); await swarmB.AddPeersAsync(new[] { swarmA.AsPeer }); IEnumerable <HashDigest <SHA256> > inventories1 = await swarmB.GetBlockHashesAsync( swarmA.AsPeer, new BlockLocator(new[] { genesis.Hash }), null); Assert.Equal( new[] { genesis.Hash, block1.Hash, block2.Hash }, inventories1); IEnumerable <HashDigest <SHA256> > inventories2 = await swarmB.GetBlockHashesAsync( swarmA.AsPeer, new BlockLocator(new[] { genesis.Hash }), block1.Hash); Assert.Equal( new[] { genesis.Hash, block1.Hash }, inventories2); List <Block <BaseAction> > receivedBlocks = await swarmB.GetBlocksAsync <BaseAction>( swarmA.AsPeer, inventories1 ).ToListAsync(); Assert.Equal( new[] { genesis, block1, block2 }, receivedBlocks); } finally { await Task.WhenAll( swarmA.StopAsync(), swarmB.StopAsync()); } }
public async Task CanBroadcastBlock() { Swarm swarmA = _swarms[0]; Swarm swarmB = _swarms[1]; Swarm swarmC = _swarms[2]; BlockChain <BaseAction> chainA = _blockchains[0]; BlockChain <BaseAction> chainB = _blockchains[1]; BlockChain <BaseAction> chainC = _blockchains[2]; // chainA, chainB and chainC shares genesis block. Block <BaseAction> genesis = chainA.MineBlock(_fx1.Address1); chainB.Append(genesis); chainC.Append(genesis); foreach (int i in Enumerable.Range(0, 10)) { chainA.MineBlock(_fx1.Address1); await Task.Delay(100); } foreach (int i in Enumerable.Range(0, 3)) { chainB.MineBlock(_fx2.Address1); await Task.Delay(100); } try { await StartAsync(swarmA, chainA); await StartAsync(swarmB, chainB); await StartAsync(swarmC, chainC); await swarmA.AddPeersAsync(new[] { swarmB.AsPeer }); await swarmA.AddPeersAsync(new[] { swarmC.AsPeer }); await EnsureExchange(swarmA, swarmB); await EnsureExchange(swarmA, swarmC); await EnsureExchange(swarmB, swarmC); await swarmB.BroadcastBlocksAsync(new[] { chainB.Last() }); await swarmC.BlockReceived.WaitAsync(); await swarmA.BlockReceived.WaitAsync(); Assert.Equal(chainB.AsEnumerable(), chainC); // chainB doesn't applied to chainA since chainB is shorter // than chainA Assert.NotEqual(chainB.AsEnumerable(), chainA); await swarmA.BroadcastBlocksAsync(new[] { chainA.Last() }); await swarmB.BlockReceived.WaitAsync(); await swarmC.BlockReceived.WaitAsync(); Assert.Equal(chainA.AsEnumerable(), chainB); Assert.Equal(chainA.AsEnumerable(), chainC); } finally { await Task.WhenAll( swarmA.StopAsync(), swarmB.StopAsync(), swarmC.StopAsync()); } }
public async Task PreloadAsyncCancellation(int cancelAfter) { Swarm <DumbAction> minerSwarm = CreateSwarm(); Swarm <DumbAction> receiverSwarm = CreateSwarm(); Log.Logger.Information("Miner: {0}", minerSwarm.Address); Log.Logger.Information("Receiver: {0}", receiverSwarm.Address); BlockChain <DumbAction> minerChain = minerSwarm.BlockChain; BlockChain <DumbAction> receiverChain = receiverSwarm.BlockChain; Guid receiverChainId = receiverChain.Id; (Address address, IEnumerable <Block <DumbAction> > blocks) = await MakeFixtureBlocksForPreloadAsyncCancellationTest(); var blockArray = blocks.ToArray(); foreach (Block <DumbAction> block in blockArray) { minerChain.Append(block); } receiverChain.Append(blockArray[0]); Assert.NotNull(minerChain.Tip); minerSwarm.FindNextHashesChunkSize = 2; await StartAsync(minerSwarm); await receiverSwarm.AddPeersAsync(new[] { minerSwarm.AsPeer }, null); CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(cancelAfter); bool canceled = true; try { await receiverSwarm.PreloadAsync(cancellationToken : cts.Token); canceled = false; Log.Logger.Debug($"{nameof(receiverSwarm.PreloadAsync)}() normally finished."); } catch (OperationCanceledException) { Log.Logger.Debug($"{nameof(receiverSwarm.PreloadAsync)}() aborted."); } catch (AggregateException ae) when(ae.InnerException is TaskCanceledException) { Log.Logger.Debug($"{nameof(receiverSwarm.PreloadAsync)}() aborted."); } cts.Dispose(); Assert.InRange(receiverChain.Store.ListChainIds().Count(), 0, 1); if (canceled) { Assert.Equal(receiverChainId, receiverChain.Id); Assert.Equal( (blockArray[0].Index, blockArray[0].Hash), (receiverChain.Tip.Index, receiverChain.Tip.Hash) ); Assert.Equal(blockArray[0], receiverChain.Tip); Assert.Equal( (Text)string.Join(",", Enumerable.Range(0, 5).Select(j => $"Item0.{j}")), receiverChain.GetState(address) ); } else { Assert.NotEqual(receiverChainId, receiverChain.Id); Assert.Equal(minerChain.Tip, receiverChain.Tip); Assert.Equal( (Text)string.Join( ",", Enumerable.Range(0, 20).Select(i => string.Join(",", Enumerable.Range(0, 5).Select(j => $"Item{i}.{j}")) ) ), receiverChain.GetState(address) ); } }