public async Task NodeStatus() { var cts = new CancellationTokenSource(); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var genesisBlock = BlockChain <EmptyAction> .MakeGenesisBlock( HashAlgorithmType.Of <SHA256>() ); // 에러로 인하여 NineChroniclesNodeService 를 사용할 수 없습니다. https://git.io/JfS0M // 따라서 LibplanetNodeService로 비슷한 환경을 맞춥니다. // 1. 노드를 생성합니다. var seedNode = CreateLibplanetNodeService <EmptyAction>(genesisBlock, apv, apvPrivateKey.PublicKey); await StartAsync(seedNode.Swarm, cts.Token); var service = CreateLibplanetNodeService <EmptyAction>(genesisBlock, apv, apvPrivateKey.PublicKey, peers: new [] { seedNode.Swarm.AsPeer }); // 2. NineChroniclesNodeService.ConfigureStandaloneContext(standaloneContext)를 호출합니다. // BlockChain 객체 공유 및 PreloadEnded, BootstrapEnded 이벤트 훅의 처리를 합니다. // BlockChain 객체 공유는 액션 타입이 달라 생략합니다. _ = service.BootstrapEnded.WaitAsync() .ContinueWith(task => StandaloneContextFx.BootstrapEnded = true); _ = service.PreloadEnded.WaitAsync() .ContinueWith(task => StandaloneContextFx.PreloadEnded = true); var bootstrapEndedTask = service.BootstrapEnded.WaitAsync(); var preloadEndedTask = service.PreloadEnded.WaitAsync(); async Task <Dictionary <string, bool> > QueryNodeStatus() { var result = await ExecuteQueryAsync("query { nodeStatus { bootstrapEnded preloadEnded } }"); var data = (Dictionary <string, object>)result.Data; var nodeStatusData = (Dictionary <string, object>)data["nodeStatus"]; return(nodeStatusData.ToDictionary(pair => pair.Key, pair => (bool)pair.Value)); } var nodeStatus = await QueryNodeStatus(); Assert.False(nodeStatus["bootstrapEnded"]); Assert.False(nodeStatus["preloadEnded"]); _ = service.StartAsync(cts.Token); await bootstrapEndedTask; await preloadEndedTask; // ContinueWith으로 넘긴 태스크가 실행되기를 기다립니다. await Task.Delay(1000); nodeStatus = await QueryNodeStatus(); Assert.True(nodeStatus["bootstrapEnded"]); Assert.True(nodeStatus["preloadEnded"]); await seedNode.StopAsync(cts.Token); }
public async Task ReadMessageCancelAsync() { var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(3)); var listener = new TcpListener(IPAddress.Any, 0); listener.Start(); var client = new TcpClient(); await client.ConnectAsync("127.0.0.1", ((IPEndPoint)listener.LocalEndpoint).Port); TcpClient listenerSocket = await listener.AcceptTcpClientAsync(); TcpTransport transport = CreateTcpTransport( appProtocolVersion: AppProtocolVersion.Sign(new PrivateKey(), 1)); try { await Assert.ThrowsAsync <TaskCanceledException>(async() => await transport.ReadMessageAsync(listenerSocket, cts.Token)); } finally { listenerSocket.Dispose(); client.Dispose(); } }
public async Task HandleDifferentAppProtocolVersion() { var isCalled = false; var signer = new PrivateKey(); AppProtocolVersion v1 = AppProtocolVersion.Sign(signer, 1); AppProtocolVersion v2 = AppProtocolVersion.Sign(signer, 2); var a = CreateSwarm( appProtocolVersion: v1, differentAppProtocolVersionEncountered: (_, ver, __) => { isCalled = true; } ); var b = CreateSwarm(appProtocolVersion: v2); try { await StartAsync(b); await Assert.ThrowsAsync <PeerDiscoveryException>(() => BootstrapAsync(a, b.AsPeer)); Assert.True(isCalled); } finally { await StopAsync(a); await StopAsync(b); a.Dispose(); b.Dispose(); } }
public void Decode() { BlockHash[] blockHashes = GenerateRandomBlockHashes(100L).ToArray(); var msg = new BlockHashes(123, blockHashes); Assert.Equal(123, msg.StartIndex); Assert.Equal(blockHashes, msg.Hashes); var privKey = new PrivateKey(); AppProtocolVersion ver = AppProtocolVersion.Sign(privKey, 3); Peer peer = new BoundPeer(privKey.PublicKey, new DnsEndPoint("0.0.0.0", 1234)); var messageCodec = new NetMQMessageCodec(); NetMQMessage encoded = messageCodec.Encode( msg, privKey, peer, DateTimeOffset.UtcNow, ver); BlockHashes restored = (BlockHashes)messageCodec.Decode( encoded, true, (b, p, a) => { }, null); Assert.Equal(msg.StartIndex, restored.StartIndex); Assert.Equal(msg.Hashes, restored.Hashes); }
public static IEnumerable <object[]> GetPeers() { var signer = new PrivateKey(); AppProtocolVersion ver = AppProtocolVersion.Sign(signer, 1); AppProtocolVersion ver2 = AppProtocolVersion.Sign( signer: signer, version: 2, extra: Bencodex.Types.Dictionary.Empty.Add("foo", 123).Add("bar", 456) ); yield return(new object[] { new Peer( new PublicKey(new byte[] { 0x04, 0xb5, 0xa2, 0x4a, 0xa2, 0x11, 0x27, 0x20, 0x42, 0x3b, 0xad, 0x39, 0xa0, 0x20, 0x51, 0x82, 0x37, 0x9d, 0x6f, 0x2b, 0x33, 0xe3, 0x48, 0x7c, 0x9a, 0xb6, 0xcc, 0x8f, 0xc4, 0x96, 0xf8, 0xa5, 0x48, 0x34, 0x40, 0xef, 0xbb, 0xef, 0x06, 0x57, 0xac, 0x2e, 0xf6, 0xc6, 0xee, 0x05, 0xdb, 0x06, 0xa9, 0x45, 0x32, 0xfd, 0xa7, 0xdd, 0xc4, 0x4a, 0x16, 0x95, 0xe5, 0xce, 0x1a, 0x3d, 0x3c, 0x76, 0xdb, })), }); }
public async Task ReadWriteMessageWithInvalidMagicCookieAsync() { byte[] invalidCookie = { 0x01, 0x02 }; Assert.False(TcpTransport.MagicCookie.SequenceEqual(invalidCookie)); var listener = new TcpListener(IPAddress.Any, 0); listener.Start(); var client = new TcpClient(); await client.ConnectAsync("127.0.0.1", ((IPEndPoint)listener.LocalEndpoint).Port); TcpClient listenerSocket = await listener.AcceptTcpClientAsync(); AppProtocolVersion version = AppProtocolVersion.Sign(new PrivateKey(), 1); TcpTransport transport = CreateTcpTransport(appProtocolVersion: version); try { var message = new Ping { Identity = Guid.NewGuid().ToByteArray(), }; var codec = new TcpMessageCodec(); byte[] serialized = codec.Encode( message, new PrivateKey(), transport.AsPeer, DateTimeOffset.UtcNow, version); int length = serialized.Length; var buffer = new byte[invalidCookie.Length + sizeof(int) + length]; invalidCookie.CopyTo(buffer, 0); BitConverter.GetBytes(length).CopyTo(buffer, invalidCookie.Length); serialized.CopyTo(buffer, invalidCookie.Length + sizeof(int)); NetworkStream stream = client.GetStream(); await stream.WriteAsync(buffer, 0, buffer.Length, default); await Assert.ThrowsAsync <InvalidMagicCookieException>( async() => await transport.ReadMessageAsync(listenerSocket, default)); byte[] shortBuffer = { 0x00 }; await stream.WriteAsync(shortBuffer, 0, shortBuffer.Length, default); await Assert.ThrowsAsync <InvalidMagicCookieException>( async() => await transport.ReadMessageAsync(listenerSocket, default)); } finally { listenerSocket.Dispose(); client.Dispose(); } }
public void String() { var signer = new PrivateKey(); AppProtocolVersion claim = AppProtocolVersion.Sign(signer, 123); Assert.Equal("123", claim.ToString()); AppProtocolVersion claimWithExtra = AppProtocolVersion.Sign(signer, 456, (Bencodex.Types.Text) "extra"); Assert.Equal("456 (Bencodex.Types.Text \"extra\")", claimWithExtra.ToString()); }
private void ConfigureNineChroniclesNodeService() { _standaloneContext.NineChroniclesNodeService = new NineChroniclesNodeService( new PrivateKey(), new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> > { GenesisBlock = _standaloneContext.BlockChain !.Genesis, StorePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), AppProtocolVersion = AppProtocolVersion.Sign(new PrivateKey(), 0), SwarmPrivateKey = new PrivateKey(), Host = IPAddress.Loopback.ToString(), },
public SwarmBenchmark() { _policy = new NullPolicy <DumbAction>(); _blocks = new List <Block <DumbAction> > { TestUtils.MineGenesis <DumbAction>(), }; _appProtocolVersion = AppProtocolVersion.Sign(new PrivateKey(), 1); _blocks.Add(TestUtils.MineNext(_blocks[0])); _blocks.Add(TestUtils.MineNext(_blocks[1])); _blocks.Add(TestUtils.MineNext(_blocks[2])); _blocks.Add(TestUtils.MineNext(_blocks[3])); }
public async Task SubscribePreloadProgress() { var cts = new CancellationTokenSource(); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var genesisBlock = BlockChain <EmptyAction> .MakeGenesisBlock( HashAlgorithmType.Of <SHA256>() ); // 에러로 인하여 NineChroniclesNodeService 를 사용할 수 없습니다. https://git.io/JfS0M // 따라서 LibplanetNodeService로 비슷한 환경을 맞춥니다. // 1. 노드를 생성합니다. var seedNode = CreateLibplanetNodeService(genesisBlock, apv, apvPrivateKey.PublicKey); await StartAsync(seedNode.Swarm, cts.Token); // 2. Progress를 넘겨 preloadProgress subscription 과 연결합니다. var service = CreateLibplanetNodeService( genesisBlock, apv, apvPrivateKey.PublicKey, new Progress <PreloadState>(state => { StandaloneContextFx.PreloadStateSubject.OnNext(state); }), new [] { seedNode.Swarm.AsPeer }); var miner = new PrivateKey(); await seedNode.BlockChain.MineBlock(miner); var result = await ExecuteQueryAsync("subscription { preloadProgress { currentPhase totalPhase extra { type currentCount totalCount } } }"); Assert.IsType <SubscriptionExecutionResult>(result); _ = service.StartAsync(cts.Token); await service.PreloadEnded.WaitAsync(cts.Token); var subscribeResult = (SubscriptionExecutionResult)result; var stream = subscribeResult.Streams.Values.FirstOrDefault(); // BlockHashDownloadState : 2 // BlockDownloadState : 1 // BlockVerificationState : 1 // ActionExecutionState : 1 const int preloadStatesCount = 5; var preloadProgressRecords = new List <(long currentPhase, long totalPhase, string type, long currentCount, long totalCount)>(); var expectedPreloadProgress = new[]
public SwarmBenchmark() { _policy = new NullPolicy <DumbAction>(); _stagePolicy = new VolatileStagePolicy <DumbAction>(); _miner = TestUtils.ChainPrivateKey; _blocks = new List <Block <DumbAction> > { TestUtils.MineGenesisBlock <DumbAction>(_policy.GetHashAlgorithm, _miner), }; _appProtocolVersion = AppProtocolVersion.Sign(new PrivateKey(), 1); _blocks.Add(TestUtils.MineNextBlock(_blocks[0], _policy.GetHashAlgorithm, _miner)); _blocks.Add(TestUtils.MineNextBlock(_blocks[1], _policy.GetHashAlgorithm, _miner)); _blocks.Add(TestUtils.MineNextBlock(_blocks[2], _policy.GetHashAlgorithm, _miner)); _blocks.Add(TestUtils.MineNextBlock(_blocks[3], _policy.GetHashAlgorithm, _miner)); }
public void DataFrames() { HashDigest <SHA256>[] blockHashes = GenerateRandomBlockHashes(100L).ToArray(); var msg = new BlockHashes(123, blockHashes); Assert.Equal(123, msg.StartIndex); Assert.Equal(blockHashes, msg.Hashes); var privKey = new PrivateKey(); AppProtocolVersion ver = AppProtocolVersion.Sign(privKey, 3); Peer peer = new BoundPeer(privKey.PublicKey, new DnsEndPoint("0.0.0.0", 1234), ver); NetMQFrame[] frames = msg.ToNetMQMessage(privKey, peer).Skip(3).ToArray(); var restored = new BlockHashes(frames); Assert.Equal(msg.StartIndex, restored.StartIndex); Assert.Equal(msg.Hashes, restored.Hashes); }
public async Task ReadWriteMessageAsync() { var listener = new TcpListener(IPAddress.Any, 0); listener.Start(); var client = new TcpClient(); await client.ConnectAsync("127.0.0.1", ((IPEndPoint)listener.LocalEndpoint).Port); TcpClient listenerSocket = await listener.AcceptTcpClientAsync(); TcpTransport transport = CreateTcpTransport( appProtocolVersion: AppProtocolVersion.Sign(new PrivateKey(), 1)); try { var message1 = new Ping { Identity = Guid.NewGuid().ToByteArray(), }; var message2 = new Pong { Identity = Guid.NewGuid().ToByteArray(), }; await transport.WriteMessageAsync(message1, client, default); await transport.WriteMessageAsync(message2, client, default); Message[] messages = new Message[2]; messages[0] = await transport.ReadMessageAsync(listenerSocket, default); messages[1] = await transport.ReadMessageAsync(listenerSocket, default); Assert.Equal(2, messages.Length); Assert.Contains(messages, message => message is Ping); Assert.Contains(messages, message => message is Pong); } finally { listenerSocket.Dispose(); client.Dispose(); } }
public async Task NodeStatusStagedTxIds() { var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var genesis = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock(); var service = ServiceBuilder.CreateNineChroniclesNodeService(genesis); StandaloneServices.ConfigureStandaloneContext(service, StandaloneContextFx); var result = await ExecuteQueryAsync("query { nodeStatus { stagedTxIds } }"); var expectedResult = new Dictionary <string, object>() { ["nodeStatus"] = new Dictionary <string, object>() { ["stagedTxIds"] = new List <object>() }, }; Assert.Null(result.Errors); Assert.Equal(expectedResult, result.Data); var tx = StandaloneContextFx.BlockChain.MakeTransaction( new PrivateKey(), new PolymorphicAction <ActionBase>[] { } ); result = await ExecuteQueryAsync("query { nodeStatus { stagedTxIds } }"); expectedResult = new Dictionary <string, object>() { ["nodeStatus"] = new Dictionary <string, object>() { ["stagedTxIds"] = new List <object> { tx.Id.ToString(), } }, }; Assert.Null(result.Errors); Assert.Equal(expectedResult, result.Data); }
public async Task DetectAppProtocolVersion() { var blockChain = _blockchains[0]; var signer = new PrivateKey(); AppProtocolVersion v2 = AppProtocolVersion.Sign(signer, 2); AppProtocolVersion v3 = AppProtocolVersion.Sign(signer, 3); var a = CreateSwarm(blockChain, appProtocolVersion: v2); var b = CreateSwarm(blockChain, appProtocolVersion: v3); var c = CreateSwarm(blockChain, appProtocolVersion: v2); var d = CreateSwarm(blockChain, appProtocolVersion: v3); try { await StartAsync(c); await StartAsync(d); var peers = new[] { c.AsPeer, d.AsPeer }; foreach (var peer in peers) { await a.AddPeersAsync(new[] { peer }, null); await b.AddPeersAsync(new[] { peer }, null); } Assert.Equal(new[] { c.AsPeer }, a.Peers.ToArray()); Assert.Equal(new[] { d.AsPeer }, b.Peers.ToArray()); } finally { await StopAsync(c); await StopAsync(d); a.Dispose(); b.Dispose(); c.Dispose(); d.Dispose(); } }
public async Task QueryAppProtocolVersion() { var fx = new DefaultStoreFixture(); var policy = new BlockPolicy <DumbAction>(); var blockchain = TestUtils.MakeBlockChain(policy, fx.Store, fx.StateStore); var apvKey = new PrivateKey(); var swarmKey = new PrivateKey(); AppProtocolVersion apv = AppProtocolVersion.Sign(apvKey, 1); string host = IPAddress.Loopback.ToString(); int port = FreeTcpPort(); using (var swarm = new Swarm <DumbAction>( blockchain, swarmKey, apv, host: host, listenPort: port)) { var peer = new BoundPeer(swarmKey.PublicKey, new DnsEndPoint(host, port)); // Before swarm starting... Assert.Throws <TimeoutException>(() => { peer.QueryAppProtocolVersion(timeout: TimeSpan.FromSeconds(1)); }); _ = swarm.StartAsync(); try { AppProtocolVersion receivedAPV = peer.QueryAppProtocolVersion(); Assert.Equal(apv, receivedAPV); } finally { await swarm.StopAsync(); } } }
public void Sign() { var signer = new PrivateKey(); PublicKey otherParty = new PrivateKey().PublicKey; AppProtocolVersion claim = AppProtocolVersion.Sign(signer, 1, null); Assert.Equal(1, claim.Version); Assert.Null(claim.Extra); Assert.True(claim.Verify(signer.PublicKey)); Assert.False(claim.Verify(otherParty)); AppProtocolVersion claimWithExtra = AppProtocolVersion.Sign(signer, 2, (Bencodex.Types.Text) "extra"); Assert.Equal(2, claimWithExtra.Version); Assert.Equal((Bencodex.Types.Text) "extra", claimWithExtra.Extra); Assert.True(claimWithExtra.Verify(signer.PublicKey)); Assert.False(claimWithExtra.Verify(otherParty)); ArgumentNullException exception = Assert.Throws <ArgumentNullException>(() => AppProtocolVersion.Sign(null, 1)); Assert.Equal("signer", exception.ParamName); }
public async Task ActivationKeyNonce_Throw_ExecutionError(string code, string msg) { var adminPrivateKey = new PrivateKey(); var adminAddress = adminPrivateKey.ToAddress(); var activatedAccounts = ImmutableHashSet <Address> .Empty; var pendingActivationStates = new List <PendingActivationState>(); Block <PolymorphicAction <ActionBase> > genesis = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock( HashAlgorithmType.Of <SHA256>(), new PolymorphicAction <ActionBase>[] { new InitializeStates( rankingState: new RankingState(), shopState: new ShopState(), gameConfigState: new GameConfigState(), redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty .Add("address", RedeemCodeState.Address.Serialize()) .Add("map", Bencodex.Types.Dictionary.Empty) ), adminAddressState: new AdminState(adminAddress, 1500000), activatedAccountsState: new ActivatedAccountsState(activatedAccounts), goldCurrencyState: new GoldCurrencyState(new Currency("NCG", 2, minter: null)), goldDistributions: new GoldDistribution[0], tableSheets: _sheets, pendingActivationStates: pendingActivationStates.ToArray() ), } ); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var userPrivateKey = new PrivateKey(); var properties = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> > { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = apv, GenesisBlock = genesis, StorePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), StoreStatesCacheSize = 2, SwarmPrivateKey = new PrivateKey(), Port = null, NoMiner = true, Render = false, Peers = ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = null, StaticPeers = ImmutableHashSet <BoundPeer> .Empty }; var blockPolicy = NineChroniclesNodeService.GetTestBlockPolicy(); var service = new NineChroniclesNodeService(userPrivateKey, properties, blockPolicy, NetworkType.Test); StandaloneContextFx.NineChroniclesNodeService = service; StandaloneContextFx.BlockChain = service.Swarm?.BlockChain; var query = $"query {{ activationKeyNonce(invitationCode: \"{code}\") }}"; var queryResult = await ExecuteQueryAsync(query); Assert.Single(queryResult.Errors); Assert.Equal(msg, queryResult.Errors.First().Message); }
public void Sign( [Argument(Name = "KEY-ID", Description = "A private key to use for signing.")] Guid keyId, [Argument(Name = "VERSION", Description = "A version number to sign.")] int version, [Option( 'p', ValueName = "PASSPHRASE", Description = "Take passphrase through this option instead of prompt." )] string?passphrase = null, [Option( 'E', ValueName = "FILE", Description = "Bencodex file to use for extra data. " + "For standard input, use a hyphen (`-'). " + "For an actual file named a hyphen, prepend `./', i.e., `./-'." )] string?extraFile = null, [Option( 'e', ValueName = "KEY=VALUE", Description = "Set a value to a key on extra Bencodex dictionary. " + "Can be applied multiple times (e.g., `-e foo=1 -e bar=baz'). " + "This option implies the extra data to be a Bencodex dictionary, " + "hence cannot be used together with -E/--extra-file option." )] string[]?extra = null ) { PrivateKey key = new KeyCommand().UnprotectKey(keyId, passphrase); IValue? extraValue = null; if (extraFile is string path) { if (extra is string[] e && e.Length > 0) { throw Utils.Error( "-E/--extra-file and -e/--extra cannot be used together at a time." ); } var codec = new Codec(); if (path == "-") { // Stream for stdin does not support .Seek() using MemoryStream buffer = new MemoryStream(); using (Stream stream = Console.OpenStandardInput()) { stream.CopyTo(buffer); } buffer.Seek(0, SeekOrigin.Begin); extraValue = codec.Decode(buffer); } else { using Stream stream = File.Open(path, FileMode.Open, FileAccess.Read); extraValue = codec.Decode(stream); } } else if (extra is string[] e && e.Length > 0) { var dict = Bencodex.Types.Dictionary.Empty; foreach (string pair in e) { int sepPos = pair.IndexOf('='); if (sepPos < 0) { throw Utils.Error( "-e/--extra must be a pair of KEY=VALUE, but no equal (=) separator: " + $"`{pair}'." ); } string key_ = pair.Substring(0, sepPos); string value = pair.Substring(sepPos + 1); dict = dict.SetItem(key_, value); } extraValue = dict; } AppProtocolVersion v = AppProtocolVersion.Sign(key, version, extraValue); Console.WriteLine(v.Token); }
public async Task ActivationStatus(bool existsActivatedAccounts) { var adminPrivateKey = new PrivateKey(); var adminAddress = adminPrivateKey.ToAddress(); var activatedAccounts = ImmutableHashSet <Address> .Empty; if (existsActivatedAccounts) { activatedAccounts = new[] { adminAddress }.ToImmutableHashSet(); } Block <PolymorphicAction <ActionBase> > genesis = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock( new PolymorphicAction <ActionBase>[] { new InitializeStates( rankingState: new RankingState(), shopState: new ShopState(), gameConfigState: new GameConfigState(), redeemCodeState: new RedeemCodeState(Bencodex.Types.Dictionary.Empty .Add("address", RedeemCodeState.Address.Serialize()) .Add("map", Bencodex.Types.Dictionary.Empty) ), adminAddressState: new AdminState(adminAddress, 1500000), activatedAccountsState: new ActivatedAccountsState(activatedAccounts), goldCurrencyState: new GoldCurrencyState(new Currency("NCG", 2, minter: null)), goldDistributions: new GoldDistribution[0], tableSheets: _sheets, pendingActivationStates: new PendingActivationState[] { } ), } ); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var userPrivateKey = new PrivateKey(); var properties = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> > { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = apv, GenesisBlock = genesis, StorePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()), StoreStatesCacheSize = 2, SwarmPrivateKey = new PrivateKey(), Port = null, MinimumDifficulty = 4096, NoMiner = true, Render = false, Peers = ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = null, StaticPeers = ImmutableHashSet <Peer> .Empty }; var service = new NineChroniclesNodeService(userPrivateKey, properties, null); StandaloneContextFx.NineChroniclesNodeService = service; StandaloneContextFx.BlockChain = service.Swarm?.BlockChain; var blockChain = StandaloneContextFx.BlockChain !; var queryResult = await ExecuteQueryAsync("query { activationStatus { activated } }"); var result = (bool)queryResult.Data .As <Dictionary <string, object> >()["activationStatus"] .As <Dictionary <string, object> >()["activated"]; // ActivatedAccounts가 비어있을때는 true이고 하나라도 있을경우 false Assert.Equal(!existsActivatedAccounts, result); var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation); blockChain.MakeTransaction(adminPrivateKey, new[] { action }); await blockChain.MineBlock(adminAddress); action = activationKey.CreateActivateAccount(nonce); blockChain.MakeTransaction(userPrivateKey, new[] { action }); await blockChain.MineBlock(adminAddress); queryResult = await ExecuteQueryAsync("query { activationStatus { activated } }"); result = (bool)queryResult.Data .As <Dictionary <string, object> >()["activationStatus"] .As <Dictionary <string, object> >()["activated"]; // ActivatedAccounts에 Address가 추가 되었기 때문에 true Assert.True(result); }
public async Task ActivateAccount() { var adminPrivateKey = new PrivateKey(); var adminAddress = adminPrivateKey.ToAddress(); var activateAccounts = new[] { adminAddress }.ToImmutableHashSet(); Block <PolymorphicAction <ActionBase> > genesis = BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock( new PolymorphicAction <ActionBase>[] { new InitializeStates() { RankingState = new RankingState(), ShopState = new ShopState(), TableSheetsState = new TableSheetsState(), GameConfigState = new GameConfigState(), RedeemCodeState = new RedeemCodeState(Bencodex.Types.Dictionary.Empty .Add("address", RedeemCodeState.Address.Serialize()) .Add("map", Bencodex.Types.Dictionary.Empty) ), AdminAddressState = new AdminState(adminAddress, 1500000), ActivatedAccountsState = new ActivatedAccountsState(activateAccounts), GoldCurrencyState = new GoldCurrencyState(new Currency("NCG", minter: null)), GoldDistributions = new GoldDistribution[0], }, } ); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var userPrivateKey = new PrivateKey(); var properties = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> > { Host = System.Net.IPAddress.Loopback.ToString(), AppProtocolVersion = apv, GenesisBlock = genesis, StorePath = null, StoreStatesCacheSize = 2, PrivateKey = userPrivateKey, Port = null, MinimumDifficulty = 4096, NoMiner = true, Render = false, Peers = ImmutableHashSet <Peer> .Empty, TrustedAppProtocolVersionSigners = null, }; var service = new NineChroniclesNodeService(properties, null); service.PrivateKey = userPrivateKey; StandaloneContextFx.NineChroniclesNodeService = service; StandaloneContextFx.BlockChain = service.Swarm.BlockChain; var blockChain = StandaloneContextFx.BlockChain; var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var privateKey = new PrivateKey(); (ActivationKey activationKey, PendingActivationState pendingActivation) = ActivationKey.Create(privateKey, nonce); PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation); blockChain.MakeTransaction(adminPrivateKey, new[] { action }); await blockChain.MineBlock(adminAddress); var encodedActivationKey = activationKey.Encode(); var queryResult = await ExecuteQueryAsync( $"mutation {{ activationStatus {{ activateAccount(encodedActivationKey: \"{encodedActivationKey}\") }} }}"); await blockChain.MineBlock(adminAddress); var result = (bool)queryResult.Data .As <Dictionary <string, object> >()["activationStatus"] .As <Dictionary <string, object> >()["activateAccount"]; Assert.True(result); var state = (Bencodex.Types.Dictionary)blockChain.GetState( ActivatedAccountsState.Address); var activatedAccountsState = new ActivatedAccountsState(state); var userAddress = userPrivateKey.ToAddress(); Assert.True(activatedAccountsState.Accounts.Contains(userAddress)); }
public void Equality() { var signer = new PrivateKey(); AppProtocolVersion claim = AppProtocolVersion.Sign(signer, 123, (Bencodex.Types.Text) "foo"); var claim2 = new AppProtocolVersion(124, claim.Extra, claim.Signature, claim.Signer); Assert.False(((IEquatable <AppProtocolVersion>)claim).Equals(claim2)); Assert.False(((object)claim).Equals(claim2)); Assert.NotEqual(claim.GetHashCode(), claim2.GetHashCode()); Assert.False(claim == claim2); Assert.True(claim != claim2); var claim3 = new AppProtocolVersion( claim.Version, Bencodex.Types.Null.Value, claim.Signature, claim.Signer ); Assert.False(((IEquatable <AppProtocolVersion>)claim).Equals(claim3)); Assert.False(((object)claim).Equals(claim3)); Assert.NotEqual(claim.GetHashCode(), claim3.GetHashCode()); Assert.False(claim == claim3); Assert.True(claim != claim3); var claim4 = new AppProtocolVersion( claim.Version, claim.Extra, ImmutableArray <byte> .Empty, claim.Signer ); Assert.False(((IEquatable <AppProtocolVersion>)claim).Equals(claim4)); Assert.False(((object)claim).Equals(claim4)); Assert.NotEqual(claim.GetHashCode(), claim4.GetHashCode()); Assert.False(claim == claim4); Assert.True(claim != claim4); var claim5 = new AppProtocolVersion( claim.Version, claim.Extra, claim.Signature, new PrivateKey().ToAddress() ); Assert.False(((IEquatable <AppProtocolVersion>)claim).Equals(claim5)); Assert.False(((object)claim).Equals(claim5)); Assert.NotEqual(claim.GetHashCode(), claim5.GetHashCode()); Assert.False(claim == claim5); Assert.True(claim != claim5); var codec = new Codec(); var sameClaim = new AppProtocolVersion( claim.Version, codec.Decode(codec.Encode(claim.Extra)), ImmutableArray.Create(claim.Signature, 0, claim.Signature.Length), new Address(claim.Signer.ByteArray) ); Assert.True(((IEquatable <AppProtocolVersion>)claim).Equals(sameClaim)); Assert.True(((object)claim).Equals(sameClaim)); Assert.Equal(claim.GetHashCode(), sameClaim.GetHashCode()); Assert.True(claim == sameClaim); Assert.False(claim != sameClaim); AppProtocolVersion claimWithoutExtra = AppProtocolVersion.Sign(signer, 1); var sameClaimWithoutExtra = new AppProtocolVersion( claimWithoutExtra.Version, claimWithoutExtra.Extra, ImmutableArray.Create( claimWithoutExtra.Signature, 0, claimWithoutExtra.Signature.Length ), new Address(claimWithoutExtra.Signer.ByteArray) ); Assert.True( ((IEquatable <AppProtocolVersion>)claimWithoutExtra).Equals(sameClaimWithoutExtra) ); Assert.True(((object)claimWithoutExtra).Equals(sameClaimWithoutExtra)); Assert.Equal(claimWithoutExtra.GetHashCode(), sameClaimWithoutExtra.GetHashCode()); Assert.True(claimWithoutExtra == sameClaimWithoutExtra); Assert.False(claimWithoutExtra != sameClaimWithoutExtra); }
public async Task QueryAppProtocolVersion(SwarmOptions.TransportType transportType) { var fx = new MemoryStoreFixture(); var policy = new BlockPolicy <DumbAction>(); var blockchain = MakeBlockChain(policy, fx.Store, fx.StateStore); var apvKey = new PrivateKey(); var swarmKey = new PrivateKey(); AppProtocolVersion apv = AppProtocolVersion.Sign(apvKey, 1); string host = IPAddress.Loopback.ToString(); int port = FreeTcpPort(); var option = new SwarmOptions { Type = transportType, }; using (var swarm = new Swarm <DumbAction>( blockchain, swarmKey, apv, host: host, listenPort: port, options: option)) { var peer = new BoundPeer(swarmKey.PublicKey, new DnsEndPoint(host, port)); // Before swarm starting... await Assert.ThrowsAsync <TimeoutException>(async() => { if (swarm.Transport is NetMQTransport) { peer.QueryAppProtocolVersionNetMQ(timeout: TimeSpan.FromSeconds(1)); } else if (swarm.Transport is TcpTransport) { await peer.QueryAppProtocolVersionTcp(timeout: TimeSpan.FromSeconds(1)); } else { throw new XunitException( "Each type of transport must have corresponding test case."); } }); _ = swarm.StartAsync(); try { AppProtocolVersion receivedAPV = default; if (swarm.Transport is NetMQTransport) { receivedAPV = peer.QueryAppProtocolVersionNetMQ(); } else if (swarm.Transport is TcpTransport) { receivedAPV = await peer.QueryAppProtocolVersionTcp(); } else { throw new XunitException( "Each type of transport must have corresponding test case."); } Assert.Equal(apv, receivedAPV); } finally { await swarm.StopAsync(); } } if (transportType == SwarmOptions.TransportType.NetMQTransport) { NetMQConfig.Cleanup(false); } }
public async Task IgnoreUntrustedAppProtocolVersion() { var signer = new PrivateKey(); AppProtocolVersion older = AppProtocolVersion.Sign(signer, 2); AppProtocolVersion newer = AppProtocolVersion.Sign(signer, 3); var untrustedSigner = new PrivateKey(); AppProtocolVersion untrustedOlder = AppProtocolVersion.Sign(untrustedSigner, 2); AppProtocolVersion untrustedNewer = AppProtocolVersion.Sign(untrustedSigner, 3); _output.WriteLine("Trusted version signer: {0}", signer.ToAddress()); _output.WriteLine("Untrusted version signer: {0}", untrustedSigner.ToAddress()); var logs = new ConcurrentDictionary <Peer, AppProtocolVersion>(); void DifferentAppProtocolVersionEncountered( Peer peer, AppProtocolVersion peerVersion, AppProtocolVersion localVersion ) { logs[peer] = peerVersion; } var a = CreateSwarm( appProtocolVersion: older, trustedAppProtocolVersionSigners: new[] { signer.PublicKey }, differentAppProtocolVersionEncountered: DifferentAppProtocolVersionEncountered ); var b = CreateSwarm( appProtocolVersion: newer, trustedAppProtocolVersionSigners: new[] { signer.PublicKey } ); var c = CreateSwarm( appProtocolVersion: older, trustedAppProtocolVersionSigners: new[] { signer.PublicKey } ); var d = CreateSwarm( appProtocolVersion: newer, trustedAppProtocolVersionSigners: new[] { signer.PublicKey } ); var e = CreateSwarm( appProtocolVersion: untrustedOlder, trustedAppProtocolVersionSigners: new[] { untrustedSigner.PublicKey } ); var f = CreateSwarm( appProtocolVersion: untrustedNewer, trustedAppProtocolVersionSigners: new[] { untrustedSigner.PublicKey } ); try { await StartAsync(c); await StartAsync(d); await StartAsync(e); await StartAsync(f); await a.AddPeersAsync(new[] { c.AsPeer }, TimeSpan.FromSeconds(1)); await a.AddPeersAsync(new[] { d.AsPeer }, TimeSpan.FromSeconds(1)); await a.AddPeersAsync(new[] { e.AsPeer }, TimeSpan.FromSeconds(1)); await a.AddPeersAsync(new[] { f.AsPeer }, TimeSpan.FromSeconds(1)); await b.AddPeersAsync(new[] { c.AsPeer }, TimeSpan.FromSeconds(1)); await b.AddPeersAsync(new[] { d.AsPeer }, TimeSpan.FromSeconds(1)); await b.AddPeersAsync(new[] { e.AsPeer }, TimeSpan.FromSeconds(1)); await b.AddPeersAsync(new[] { f.AsPeer }, TimeSpan.FromSeconds(1)); Assert.Equal(new[] { c.AsPeer }, a.Peers.ToArray()); Assert.Equal(new[] { d.AsPeer }, b.Peers.ToArray()); _output.WriteLine("Logged encountered peers:"); foreach (KeyValuePair <Peer, AppProtocolVersion> kv in logs) { _output.WriteLine( "{0}; {1}; {2} -> {3}", kv.Key, kv.Value.Version, kv.Value.Signer, kv.Value.Verify(signer.PublicKey) ? "verified" : "not verified" ); } } finally { await StopAsync(c); await StopAsync(d); await StopAsync(e); await StopAsync(f); a.Dispose(); b.Dispose(); c.Dispose(); d.Dispose(); e.Dispose(); f.Dispose(); } }
public static IEnumerable <object[]> GetPeers() { var signer = new PrivateKey(); AppProtocolVersion ver = AppProtocolVersion.Sign(signer, 1); yield return(new object[] { new BoundPeer( new PublicKey(new byte[] { 0x04, 0xb5, 0xa2, 0x4a, 0xa2, 0x11, 0x27, 0x20, 0x42, 0x3b, 0xad, 0x39, 0xa0, 0x20, 0x51, 0x82, 0x37, 0x9d, 0x6f, 0x2b, 0x33, 0xe3, 0x48, 0x7c, 0x9a, 0xb6, 0xcc, 0x8f, 0xc4, 0x96, 0xf8, 0xa5, 0x48, 0x34, 0x40, 0xef, 0xbb, 0xef, 0x06, 0x57, 0xac, 0x2e, 0xf6, 0xc6, 0xee, 0x05, 0xdb, 0x06, 0xa9, 0x45, 0x32, 0xfd, 0xa7, 0xdd, 0xc4, 0x4a, 0x16, 0x95, 0xe5, 0xce, 0x1a, 0x3d, 0x3c, 0x76, 0xdb, }), new DnsEndPoint("0.0.0.0", 1234), ver, IPAddress.IPv6Loopback), }); yield return(new object[] { new BoundPeer( new PublicKey(new byte[] { 0x04, 0xb5, 0xa2, 0x4a, 0xa2, 0x11, 0x27, 0x20, 0x42, 0x3b, 0xad, 0x39, 0xa0, 0x20, 0x51, 0x82, 0x37, 0x9d, 0x6f, 0x2b, 0x33, 0xe3, 0x48, 0x7c, 0x9a, 0xb6, 0xcc, 0x8f, 0xc4, 0x96, 0xf8, 0xa5, 0x48, 0x34, 0x40, 0xef, 0xbb, 0xef, 0x06, 0x57, 0xac, 0x2e, 0xf6, 0xc6, 0xee, 0x05, 0xdb, 0x06, 0xa9, 0x45, 0x32, 0xfd, 0xa7, 0xdd, 0xc4, 0x4a, 0x16, 0x95, 0xe5, 0xce, 0x1a, 0x3d, 0x3c, 0x76, 0xdb, }), new DnsEndPoint("0.0.0.0", 1234), ver), }); yield return(new object[] { new BoundPeer( new PublicKey(new byte[] { 0x04, 0xb5, 0xa2, 0x4a, 0xa2, 0x11, 0x27, 0x20, 0x42, 0x3b, 0xad, 0x39, 0xa0, 0x20, 0x51, 0x82, 0x37, 0x9d, 0x6f, 0x2b, 0x33, 0xe3, 0x48, 0x7c, 0x9a, 0xb6, 0xcc, 0x8f, 0xc4, 0x96, 0xf8, 0xa5, 0x48, 0x34, 0x40, 0xef, 0xbb, 0xef, 0x06, 0x57, 0xac, 0x2e, 0xf6, 0xc6, 0xee, 0x05, 0xdb, 0x06, 0xa9, 0x45, 0x32, 0xfd, 0xa7, 0xdd, 0xc4, 0x4a, 0x16, 0x95, 0xe5, 0xce, 0x1a, 0x3d, 0x3c, 0x76, 0xdb, }), new DnsEndPoint("0.0.0.0", 1234), ver), }); yield return(new object[] { new Peer( new PublicKey(new byte[] { 0x04, 0xb5, 0xa2, 0x4a, 0xa2, 0x11, 0x27, 0x20, 0x42, 0x3b, 0xad, 0x39, 0xa0, 0x20, 0x51, 0x82, 0x37, 0x9d, 0x6f, 0x2b, 0x33, 0xe3, 0x48, 0x7c, 0x9a, 0xb6, 0xcc, 0x8f, 0xc4, 0x96, 0xf8, 0xa5, 0x48, 0x34, 0x40, 0xef, 0xbb, 0xef, 0x06, 0x57, 0xac, 0x2e, 0xf6, 0xc6, 0xee, 0x05, 0xdb, 0x06, 0xa9, 0x45, 0x32, 0xfd, 0xa7, 0xdd, 0xc4, 0x4a, 0x16, 0x95, 0xe5, 0xce, 0x1a, 0x3d, 0x3c, 0x76, 0xdb, }), ver), }); }
public async Task SubscribeDifferentAppProtocolVersionEncounter() { var result = await ExecuteQueryAsync(@" subscription { differentAppProtocolVersionEncounter { peer peerVersion { version signer signature extra } localVersion { version signer signature extra } } } "); var subscribeResult = (SubscriptionExecutionResult)result; var stream = subscribeResult.Streams.Values.FirstOrDefault(); Assert.NotNull(stream); Assert.ThrowsAsync <OperationCanceledException>(async() => { await stream.Take(1).Timeout(TimeSpan.FromMilliseconds(5000)).FirstAsync(); }); var apvPrivateKey = new PrivateKey(); var apv1 = AppProtocolVersion.Sign(apvPrivateKey, 1); var apv2 = AppProtocolVersion.Sign(apvPrivateKey, 0); var peer = new Peer(apvPrivateKey.PublicKey, apv1); StandaloneContextFx.DifferentAppProtocolVersionEncounterSubject.OnNext( new DifferentAppProtocolVersionEncounter { Peer = peer, PeerVersion = apv1, LocalVersion = apv2, } ); var rawEvents = await stream.Take(1); var rawEvent = (Dictionary <string, object>)rawEvents.Data; var differentAppProtocolVersionEncounter = (Dictionary <string, object>)rawEvent["differentAppProtocolVersionEncounter"]; Assert.Equal( peer.ToString(), differentAppProtocolVersionEncounter["peer"] ); var peerVersion = (Dictionary <string, object>)differentAppProtocolVersionEncounter["peerVersion"]; Assert.Equal(apv1.Version, peerVersion["version"]); Assert.Equal(apv1.Signer, new Address(((string)peerVersion["signer"]).Substring(2))); Assert.Equal(apv1.Signature, ByteUtil.ParseHex((string)peerVersion["signature"])); Assert.Equal(apv1.Extra, peerVersion["extra"]); var localVersion = (Dictionary <string, object>)differentAppProtocolVersionEncounter["localVersion"]; Assert.Equal(apv2.Version, localVersion["version"]); Assert.Equal(apv2.Signer, new Address(((string)localVersion["signer"]).Substring(2))); Assert.Equal(apv2.Signature, ByteUtil.ParseHex((string)localVersion["signature"])); Assert.Equal(apv2.Extra, localVersion["extra"]); }
void OnGUI() { _showPrivateKey = EditorGUILayout.Foldout(_showPrivateKey, "Private Key"); if (_showPrivateKey) { _selectedPrivateKeyIndex = EditorGUILayout.Popup( "Private Key", _selectedPrivateKeyIndex, _privateKeyOptions); if (_selectedPrivateKeyIndex == _privateKeyOptions.Length - 1) { _toggledOnTypePrivateKey = EditorGUILayout.Toggle( "Type New Private Key", _toggledOnTypePrivateKey); if (_toggledOnTypePrivateKey) { _privateKey = EditorGUILayout.PasswordField("New Private Key", _privateKey) ?? string.Empty; ShowError(_privateKey.Any() ? null : "New private key is empty."); } } _privateKeyPassphrase = EditorGUILayout.PasswordField("Passphrase", _privateKeyPassphrase) ?? string.Empty; ShowError(_privateKeyPassphrase.Any() ? null : "Passphrase is empty."); if (_selectedPrivateKeyIndex == _privateKeyOptions.Length - 1) { EditorGUI.BeginDisabledGroup(!_privateKeyPassphrase.Any()); if (GUILayout.Button("Create")) { var privateKey = _toggledOnTypePrivateKey ? new PrivateKey(ByteUtil.ParseHex(_privateKey)) : new PrivateKey(); var ppk = ProtectedPrivateKey.Protect(privateKey, _privateKeyPassphrase); _keyStore.Add(ppk); RefreshPrivateKeys(); _selectedPrivateKeyIndex = Array.IndexOf(_privateKeys, privateKey); } EditorGUI.EndDisabledGroup(); } } HorizontalLine(); _showParameters = EditorGUILayout.Foldout(_showParameters, "Parameters"); if (_showParameters) { _versionString = EditorGUILayout.TextField("Version", _versionString); try { version = int.Parse(_versionString, CultureInfo.InvariantCulture); } catch (Exception e) { ShowError(e.Message); } macOSBinaryUrl = EditorGUILayout.TextField("macOS Binary URL", macOSBinaryUrl); windowsBinaryUrl = EditorGUILayout.TextField("Windows Binary URL", windowsBinaryUrl); } HorizontalLine(); EditorGUI.BeginDisabledGroup( !(_privateKeyPassphrase.Any() && _selectedPrivateKeyIndex < _privateKeyOptions.Length - 1)); if (GUILayout.Button("Sign")) { var appProtocolVersionExtra = new AppProtocolVersionExtra(macOSBinaryUrl, windowsBinaryUrl, _timestamp); PrivateKey key; try { key = _privateKeys[_selectedPrivateKeyIndex].Item2 .Unprotect(_privateKeyPassphrase); } catch (IncorrectPassphraseException) { EditorUtility.DisplayDialog( "Unmatched passphrase", "Private key passphrase is incorrect.", "Retype passphrase" ); _privateKeyPassphrase = string.Empty; return; } _appProtocolVersion = AppProtocolVersion.Sign( key, version, appProtocolVersionExtra.Serialize()); } EditorGUI.EndDisabledGroup(); if (_appProtocolVersion is AppProtocolVersion v) { GUILayout.TextArea(v.Token); } }
public async Task SubscribePreloadProgress() { var cts = new CancellationTokenSource(); var apvPrivateKey = new PrivateKey(); var apv = AppProtocolVersion.Sign(apvPrivateKey, 0); var genesisBlock = BlockChain <EmptyAction> .MakeGenesisBlock(); // 에러로 인하여 NineChroniclesNodeService 를 사용할 수 없습니다. https://git.io/JfS0M // 따라서 LibplanetNodeService로 비슷한 환경을 맞춥니다. // 1. 노드를 생성합니다. var seedNode = CreateLibplanetNodeService <EmptyAction>(genesisBlock, apv, apvPrivateKey.PublicKey); await StartAsync(seedNode.Swarm, cts.Token); // 2. Progress를 넘겨 preloadProgress subscription 과 연결합니다. var service = CreateLibplanetNodeService <EmptyAction>( genesisBlock, apv, apvPrivateKey.PublicKey, new Progress <PreloadState>(state => { StandaloneContextFx.PreloadStateSubject.OnNext(state); }), new [] { seedNode.Swarm.AsPeer }); var miner = new PrivateKey().ToAddress(); await seedNode.BlockChain.MineBlock(miner); var result = await ExecuteQueryAsync("subscription { preloadProgress { currentPhase totalPhase extra { type currentCount totalCount } } }"); Assert.IsType <SubscriptionExecutionResult>(result); service.StartAsync(cts.Token); await service.PreloadEnded.WaitAsync(cts.Token); var subscribeResult = (SubscriptionExecutionResult)result; var stream = subscribeResult.Streams.Values.FirstOrDefault(); // BlockHashDownloadState : 1 // BlockDownloadState : 1 // BlockVerificationState : 1 // ActionExecutionState : 1 const int preloadStatesCount = 4; var preloadProgressRecords = new List <(long currentPhase, long totalPhase, string type, long currentCount, long totalCount)>(); var expectedPreloadProgress = new List <(long currentPhase, long totalPhase, string type, long currentCount, long totalCount)> { (1, 5, "BlockHashDownloadState", 1, 1), (2, 5, "BlockDownloadState", 1, 1), (3, 5, "BlockVerificationState", 1, 1), (5, 5, "ActionExecutionState", 1, 1), }; foreach (var index in Enumerable.Range(1, preloadStatesCount)) { var rawEvents = await stream.Take(index); var preloadProgress = (Dictionary <string, object>)((Dictionary <string, object>)rawEvents.Data)["preloadProgress"]; var preloadProgressExtra = (Dictionary <string, object>)preloadProgress["extra"]; preloadProgressRecords.Add(( (long)preloadProgress["currentPhase"], (long)preloadProgress["totalPhase"], (string)preloadProgressExtra["type"], (long)preloadProgressExtra["currentCount"], (long)preloadProgressExtra["totalCount"])); } Assert.True(preloadProgressRecords.ToImmutableHashSet().SetEquals(expectedPreloadProgress)); await seedNode.StopAsync(cts.Token); await service.StopAsync(cts.Token); }