public virtual void BlocksBeingAppended() { var blockLogs = new List <(Block <DumbAction> OldTip, Block <DumbAction> NewTip)>(); uint reorgs = 0; var innerRenderer = new AnonymousRenderer <DumbAction> { BlockRenderer = (oldTip, newTip) => blockLogs.Add((oldTip, newTip)), ReorgRenderer = (oldTip, newTip, bp) => { // Note that this callback should not be invoked in this test case. reorgs++; }, }; var renderer = new DelayedRenderer <DumbAction>( innerRenderer, _canonicalChainComparer, _store, _ => HashAlgorithmType.Of <SHA256>(), // thunk getter; doesn't matter here confirmations: 3 ); Assert.Null(renderer.Tip); Assert.Empty(blockLogs); Assert.Equal(0U, reorgs); // #0 -> 1 confirm; #1 -> no confirms; #2 -> no confirms renderer.RenderBlock(_chainA[0], _chainA[1]); Assert.Null(renderer.Tip); Assert.Empty(blockLogs); Assert.Equal(0U, reorgs); // #0 -> 2 confirms; #1 -> 1 confirm; #2 -> no confirms renderer.RenderBlock(_chainA[1], _chainA[2]); Assert.Null(renderer.Tip); Assert.Empty(blockLogs); Assert.Equal(0U, reorgs); // #0 -> 3 confirms; #1 -> 2 confirms; #2 -> 1 confirm; tip changed -> #0 renderer.RenderBlock(_chainA[2], _chainA[3]); Assert.Equal(_chainA[0], renderer.Tip); Assert.Empty(blockLogs); Assert.Equal(0U, reorgs); // #0 -> gone; #1 -> 3 confirms; #2 -> 2 confirms; tip changed -> #1; render(#0, #1) renderer.RenderBlock(_chainA[3], _chainA[4]); Assert.Equal(_chainA[1], renderer.Tip); Assert.Single(blockLogs); Assert.Equal((_chainA[0], _chainA[1]), blockLogs[0]); Assert.Equal(0U, reorgs); // #0 -> gone; #1 -> gone; #2 -> 3 confirms; tip changed -> #2; render(#1, #2) renderer.RenderBlock(_chainA[4], _chainA[5]); Assert.Equal(_chainA[2], renderer.Tip); Assert.Equal(2, blockLogs.Count); Assert.Equal((_chainA[0], _chainA[1]), blockLogs[0]); Assert.Equal((_chainA[1], _chainA[2]), blockLogs[1]); Assert.Equal(0U, reorgs); }
public BlockChainService( BlockChain <NCAction> blockChain, Swarm <NCAction> swarm, RpcContext context, LibplanetNodeServiceProperties <NCAction> libplanetNodeServiceProperties ) { _blockChain = blockChain; _delayedRenderer = blockChain.GetDelayedRenderer(); _swarm = swarm; _context = context; _codec = new Codec(); _libplanetNodeServiceProperties = libplanetNodeServiceProperties; }
public BlockChainService( BlockChain <NineChroniclesActionType> blockChain, Swarm <NineChroniclesActionType> swarm, RpcContext context, LibplanetNodeServiceProperties <NineChroniclesActionType> libplanetNodeServiceProperties ) { _blockChain = blockChain; _delayedRenderer = blockChain.Renderers .OfType <DelayedRenderer <NineChroniclesActionType> >() .FirstOrDefault(); _swarm = swarm; _context = context; _codec = new Codec(); _libplanetNodeServiceProperties = libplanetNodeServiceProperties; }
public virtual void BlocksBeingAppendedInParallel() { var blockLogs = new List <(Block <DumbAction> OldTip, Block <DumbAction> NewTip)>(); var reorgLogs = new List <( Block <DumbAction> OldTip, Block <DumbAction> NewTip, Block <DumbAction> Branchpoint )>(); var innerRenderer = new AnonymousRenderer <DumbAction> { BlockRenderer = (oldTip, newTip) => blockLogs.Add((oldTip, newTip)), ReorgRenderer = (oldTip, newTip, bp) => reorgLogs.Add((oldTip, newTip, bp)), }; var delayedRenderer = new DelayedRenderer <DumbAction>( innerRenderer, _canonicalChainComparer, _store, confirmations: 3 ); var renderer = new LoggedRenderer <DumbAction>( delayedRenderer, _logger, LogEventLevel.Verbose ); Assert.Null(delayedRenderer.Tip); Assert.Empty(blockLogs); Assert.Empty(reorgLogs); // Some explanation on the fixture: there are two chains that shares a certain block // as a branchpoint: _chainA and _chainB. The topmost mutual block, i.e., branchpoint, // between _chainA and _chainB is _chainA[4] (== _chainB[4]). // In this test, we tries to simulate blocks from two parallel chains being "appended" // (discovered) to a peer. The order of discovery can be drawn as below (the prime // [apostrophe] after the block index number like #N' shows it's from _chainB; the bare // block index without that like #N means it's from _chainA): // // #4 (1st) // | // / \ // #5 (2nd) #5' (3rd) // | | // #6 (3rd) #6' (4th) // | | // #7 (5th) #7' (6th) // | | // #8 (7th) #8' (8th) // | // #9' (9th) // #4 -> 1 confirm // #5 -> no confirms // #5' -> no confirms renderer.RenderBlock(_chainA[4], _chainA[5]); var expectedBlockLogs = new List <(Block <DumbAction> OldTip, Block <DumbAction> NewTip)> { (_chainA[1], _chainA[2]), }; Assert.Equal(_chainA[2], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 1 confirms // #5 -> no confirms // #5' -> no confirms renderer.RenderReorg(_chainA[5], _chainB[5], _branchpoint); renderer.RenderBlock(_chainA[5], _chainB[5]); renderer.RenderReorgEnd(_chainA[5], _chainB[5], _branchpoint); Assert.Equal(_chainA[2], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 2 confirms; tip changed -> #3 // #5 -> 1 confirm; #6 -> no confirm // #5' -> no confirms renderer.RenderReorg(_chainB[5], _chainA[6], _branchpoint); renderer.RenderBlock(_chainB[5], _chainA[6]); renderer.RenderReorgEnd(_chainB[5], _chainA[6], _branchpoint); expectedBlockLogs.Add((_chainA[2], _chainA[3])); Assert.Equal(_chainA[3], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 2 confirms // #5 -> 1 confirm; #6 -> no confirm // #5' -> 1 confirm; #6' -> no confirm renderer.RenderReorg(_chainA[6], _chainB[6], _branchpoint); renderer.RenderBlock(_chainA[6], _chainB[6]); renderer.RenderReorgEnd(_chainA[6], _chainB[6], _branchpoint); Assert.Equal(_chainA[3], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> 3 confirms; tip changed -> #4 // #5 -> 2 confirms; #6 -> 1 confirm; #7 -> no confirm // #5' -> 1 confirm; #6' -> no confirm renderer.RenderReorg(_chainB[6], _chainA[7], _branchpoint); renderer.RenderBlock(_chainB[6], _chainA[7]); renderer.RenderReorgEnd(_chainB[6], _chainA[7], _branchpoint); expectedBlockLogs.Add((_chainA[3], _chainA[4])); Assert.Equal(_chainA[4], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> gone but still is tip // #5 -> 2 confirms; #6 -> 1 confirm; #7 -> no confirm // #5' -> 2 confirms; #6' -> 1 confirm; #7' -> no confirm renderer.RenderReorg(_chainA[7], _chainB[7], _branchpoint); renderer.RenderBlock(_chainA[7], _chainB[7]); renderer.RenderReorgEnd(_chainA[7], _chainB[7], _branchpoint); Assert.Equal(_chainA[4], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // #4 -> gone; tip changed -> #5; render(#4, #5) // #5 -> 3 confirms; #6 -> 2 confirms; #7 -> 1 confirm; #8 -> no confirm // #5' -> 2 confirms; #6' -> 1 confirm; #7' -> no confirm renderer.RenderReorg(_chainB[7], _chainA[8], _branchpoint); renderer.RenderBlock(_chainB[7], _chainA[8]); renderer.RenderReorgEnd(_chainB[7], _chainA[8], _branchpoint); expectedBlockLogs.Add((_chainA[4], _chainA[5])); Assert.Equal(_chainA[5], delayedRenderer.Tip); Assert.Empty(reorgLogs); Assert.Equal(expectedBlockLogs, blockLogs); // tip changed -> #5'; render(#5, #5'); reorg(#5, #5', #4) // #5 -> 3 confirms; #6 -> 2 confirms; #7 -> 1 confirm; #8 -> no confirm // #5' -> 3 confirms; #6' -> 2 confirms; #7' -> 1 confirm; #8' -> no confirm renderer.RenderReorg(_chainA[8], _chainB[8], _branchpoint); renderer.RenderBlock(_chainA[8], _chainB[8]); renderer.RenderReorgEnd(_chainA[8], _chainB[8], _branchpoint); expectedBlockLogs.Add((_chainA[5], _chainB[5])); Assert.Equal(_chainB[5], delayedRenderer.Tip); Assert.Equal(new[] { (_chainA[5], _chainB[5], _branchpoint) }, reorgLogs);
public StandaloneQuery(StandaloneContext standaloneContext, IConfiguration configuration) { bool useSecretToken = configuration[GraphQLService.SecretTokenKey] is { }; Field <NonNullGraphType <StateQuery> >(name: "stateQuery", arguments: new QueryArguments( new QueryArgument <ByteStringType> { Name = "hash", Description = "Offset block hash for query.", }), resolve: context => { BlockHash?blockHash = context.GetArgument <byte[]>("hash") switch { byte[] bytes => new BlockHash(bytes), null => null, }; if (standaloneContext.BlockChain is { } blockChain) { DelayedRenderer <NCAction>?delayedRenderer = blockChain.GetDelayedRenderer(); blockHash = delayedRenderer?.Tip?.Hash; } return(standaloneContext.BlockChain?.ToAccountStateGetter(blockHash), standaloneContext.BlockChain?.ToAccountBalanceGetter(blockHash)); } ); Field <ByteStringType>( name: "state", arguments: new QueryArguments( new QueryArgument <NonNullGraphType <AddressType> > { Name = "address", Description = "The address of state to fetch from the chain." }, new QueryArgument <ByteStringType> { Name = "hash", Description = "The hash of the block used to fetch state from chain." } ), resolve: context => { if (!(standaloneContext.BlockChain is BlockChain <PolymorphicAction <ActionBase> > blockChain)) { throw new ExecutionError( $"{nameof(StandaloneContext)}.{nameof(StandaloneContext.BlockChain)} was not set yet!"); } var address = context.GetArgument <Address>("address"); var blockHashByteArray = context.GetArgument <byte[]>("hash"); var blockHash = blockHashByteArray is null ? blockChain.Tip.Hash : new BlockHash(blockHashByteArray); var state = blockChain.GetState(address, blockHash); return(new Codec().Encode(state)); } ); Field <NonNullGraphType <ListGraphType <NonNullGraphType <TransferNCGHistoryType> > > >( "transferNCGHistories", arguments: new QueryArguments( new QueryArgument <NonNullGraphType <ByteStringType> > { Name = "blockHash" }, new QueryArgument <AddressType> { Name = "recipient" } ), resolve: context => { BlockHash blockHash = new BlockHash(context.GetArgument <byte[]>("blockHash")); if (!(standaloneContext.Store is { } store)) { throw new InvalidOperationException(); } if (!(store.GetBlock <NCAction>(blockHash) is { } block)) { throw new ArgumentException("blockHash"); } var recipient = context.GetArgument <Address?>("recipient"); var filteredTransactions = block.Transactions.Where(tx => tx.Actions.Count == 1 && tx.Actions.First().InnerAction is TransferAsset transferAsset && (!recipient.HasValue || transferAsset.Recipient == recipient) && transferAsset.Amount.Currency.Ticker == "NCG" && store.GetTxExecution(blockHash, tx.Id) is TxSuccess); TransferNCGHistory ToTransferNCGHistory(TxSuccess txSuccess, string memo) { var rawTransferNcgHistories = txSuccess.FungibleAssetsDelta.Select(pair => (pair.Key, pair.Value.Values.First(fav => fav.Currency.Ticker == "NCG"))) .ToArray(); var((senderAddress, _), (recipientAddress, amount)) = rawTransferNcgHistories[0].Item2.RawValue > rawTransferNcgHistories[1].Item2.RawValue ? (rawTransferNcgHistories[1], rawTransferNcgHistories[0]) : (rawTransferNcgHistories[0], rawTransferNcgHistories[1]); return(new TransferNCGHistory( txSuccess.BlockHash, txSuccess.TxId, senderAddress, recipientAddress, amount, memo)); } var histories = filteredTransactions.Select(tx => ToTransferNCGHistory((TxSuccess)store.GetTxExecution(blockHash, tx.Id), tx.Actions.Single().InnerAction.As <TransferAsset>().Memo)); return(histories); });