Skip to content

Commit 3ac2128

Browse files
committed
Add GetBlockFromPeer
1 parent bc31ee7 commit 3ac2128

File tree

7 files changed

+88
-5
lines changed

7 files changed

+88
-5
lines changed

NBitcoin.TestFramework/NodeBuilder.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ public Network Network
221221
public RPCWalletType? RPCWalletType { get; set; }
222222
public bool CreateWallet { get; set; } = true;
223223

224+
/// <summary>
225+
/// All nodes will be whitebind (default: false)
226+
/// </summary>
227+
public bool WhiteBind { get; set; }
228+
224229
public CoreNode CreateNode(bool start = false)
225230
{
226231
var child = Path.Combine(_Root, last.ToString());
@@ -295,7 +300,7 @@ public CoreNode(string folder, NodeBuilder builder)
295300
this._Builder = builder;
296301
this._Folder = folder;
297302
_State = CoreNodeState.Stopped;
298-
303+
this.WhiteBind = builder.WhiteBind;
299304
dataDir = Path.Combine(folder, "data");
300305
var pass = Hashes.DoubleSHA256(Encoding.UTF8.GetBytes(folder)).ToString();
301306
creds = new NetworkCredential(pass, pass);
@@ -391,10 +396,11 @@ public void Sync(CoreNode node, bool keepConnection = false)
391396
rpc.AddNode(node.Endpoint, true);
392397
while (rpc.GetBestBlockHash() != rpc1.GetBestBlockHash())
393398
{
399+
rpc.Generate(1);
394400
Task.Delay(200).GetAwaiter().GetResult();
395401
}
396402
if (!keepConnection)
397-
rpc.RemoveNode(node.Endpoint);
403+
rpc.DisconnectNode(node.Endpoint).GetAwaiter().GetResult();
398404
}
399405
#endif
400406
private CoreNodeState _State;

NBitcoin.Tests/RPCClientTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using static NBitcoin.Tests.Comparer;
2222
using System.Net.Http;
2323
using NBitcoin.Scripting;
24+
using NBitcoin.Protocol;
2425

2526
namespace NBitcoin.Tests
2627
{
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace NBitcoin.RPC
8+
{
9+
public enum GetBlockFromPeerResult
10+
{
11+
/// <summary>
12+
/// Successfully fetched the block from the peer
13+
/// </summary>
14+
Fetched,
15+
/// <summary>
16+
/// Peer does not exist
17+
/// </summary>
18+
UnknownPeerId,
19+
/// <summary>
20+
/// Block already downloaded
21+
/// </summary>
22+
AlreadyDownloaded,
23+
/// <summary>
24+
/// Block header missing
25+
/// </summary>
26+
BlockHeaderMissing,
27+
/// <summary>
28+
/// In prune mode, only blocks that the node has already synced previously can be fetched from a peer
29+
/// </summary>
30+
NeverSynched
31+
}
32+
}

NBitcoin/RPC/RPCCapabilities.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class RPCCapabilities
1717
public bool SupportEstimateSmartFee { get; set; }
1818
public bool SupportGenerateToAddress { get; set; }
1919
public bool SupportTestMempoolAccept { get; set; }
20+
public bool CanGetBlockFromPeer { get; set; }
2021

2122
public RPCCapabilities Clone(int newVersion)
2223
{
@@ -29,7 +30,8 @@ public RPCCapabilities Clone(int newVersion)
2930
SupportSignRawTransactionWith = SupportSignRawTransactionWith,
3031
SupportGetNetworkInfo = SupportGetNetworkInfo,
3132
SupportEstimateSmartFee = SupportEstimateSmartFee,
32-
SupportGenerateToAddress = SupportGenerateToAddress
33+
SupportGenerateToAddress = SupportGenerateToAddress,
34+
CanGetBlockFromPeer = CanGetBlockFromPeer
3335
};
3436
}
3537

@@ -42,7 +44,8 @@ public override string ToString()
4244
$"SupportSignRawTransactionWith: {SupportSignRawTransactionWith}{Environment.NewLine}" +
4345
$"SupportGetNetworkInfo: {SupportGetNetworkInfo}{Environment.NewLine}" +
4446
$"SupportEstimateSmartFee: {SupportEstimateSmartFee}{Environment.NewLine}" +
45-
$"SupportGenerateToAddress: {SupportGenerateToAddress}{Environment.NewLine}";
47+
$"SupportGenerateToAddress: {SupportGenerateToAddress}{Environment.NewLine}" +
48+
$"CanGetBlockFromPeer: {CanGetBlockFromPeer}{Environment.NewLine} ";
4649
}
4750
}
4851
}

NBitcoin/RPC/RPCClient.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ public async Task<RPCCapabilities> ScanRPCCapabilitiesAsync(CancellationToken ca
320320
CheckCapabilitiesAsync(rpc, "scantxoutset", v => capabilities.SupportScanUTXOSet = v, cancellationToken),
321321
CheckCapabilitiesAsync(rpc, "signrawtransactionwithkey", v => capabilities.SupportSignRawTransactionWith = v, cancellationToken),
322322
CheckCapabilitiesAsync(rpc, "testmempoolaccept", v => capabilities.SupportTestMempoolAccept = v, cancellationToken),
323+
CheckCapabilitiesAsync(rpc, "getblockfrompeer", v => capabilities.CanGetBlockFromPeer = v, cancellationToken),
323324
CheckCapabilitiesAsync(rpc, "estimatesmartfee", v => capabilities.SupportEstimateSmartFee = v, cancellationToken),
324325
CheckCapabilitiesAsync(rpc, "generatetoaddress", v => capabilities.SupportGenerateToAddress = v, cancellationToken),
325326
CheckSegwitCapabilitiesAsync(rpc, v => capabilities.SupportSegwit = v, ScriptPubKeyType.Segwit, cancellationToken),
@@ -581,6 +582,32 @@ public async Task<BitcoinAddress> GetNewAddressAsync(CancellationToken cancellat
581582
return BitcoinAddress.Create(result.Result.ToString(), Network);
582583
}
583584

585+
/// <summary>
586+
/// Attempt to fetch block from a given peer.
587+
/// </summary>
588+
/// <param name="blockHash">The block hash to try to fetch</param>
589+
/// <param name="peerId">The peer to fetch it from (see <see cref="GetPeersInfo"></see> for peer IDs)</param>
590+
/// <param name="cancellationToken"></param>
591+
/// <exception cref="RPCException">Fetching the block wasn't successful, for other reason</exception>
592+
/// <returns>The result of this operation. You can then query it with <see cref="GetBlockAsync(uint256, CancellationToken)"/>. If the result is unknown <see cref="RPCException"></see> is thrown./></returns>
593+
public async Task<GetBlockFromPeerResult> GetBlockFromPeer(uint256 blockHash, int peerId, CancellationToken cancellationToken = default)
594+
{
595+
if (blockHash is null)
596+
throw new ArgumentNullException(nameof(blockHash));
597+
var r = new RPCRequest(RPCOperations.getblockfrompeer, new object[] { blockHash.ToString(), peerId });
598+
r.ThrowIfRPCError = false;
599+
var result = await SendCommandAsync(r, cancellationToken).ConfigureAwait(false);
600+
return result.Error switch
601+
{
602+
null => RPC.GetBlockFromPeerResult.Fetched,
603+
{ Message: "Block already downloaded" } => RPC.GetBlockFromPeerResult.AlreadyDownloaded,
604+
{ Message: "Block header missing" } => RPC.GetBlockFromPeerResult.BlockHeaderMissing,
605+
{ Message: "Peer does not exist" } => RPC.GetBlockFromPeerResult.UnknownPeerId,
606+
{ Message: "In prune mode, only blocks that the node has already synced previously can be fetched from a peer" } => RPC.GetBlockFromPeerResult.NeverSynched,
607+
{ } e => throw new RPCException(e.Code, e.Message, result)
608+
};
609+
}
610+
584611
public async Task<BitcoinAddress> GetNewAddressAsync(GetNewAddressRequest request, CancellationToken cancellationToken = default)
585612
{
586613
var p = new Dictionary<string, object>();
@@ -1095,6 +1122,7 @@ public async Task<PeerInfo[]> GetPeersInfoAsync(CancellationToken cancellationTo
10951122
Address = addressEnpoint,
10961123
LocalAddress = localEndpoint,
10971124
Services = (NodeServices)services,
1125+
ServicesNames = (peer["servicesnames"] as JArray)?.Select(a => a.ToString()).ToArray(),
10981126
LastSend = Utils.UnixTimeToDateTime((uint)peer["lastsend"]),
10991127
LastReceive = Utils.UnixTimeToDateTime((uint)peer["lastrecv"]),
11001128
BytesSent = (long)peer["bytessent"],
@@ -1119,6 +1147,11 @@ public async Task<PeerInfo[]> GetPeersInfoAsync(CancellationToken cancellationTo
11191147
return result;
11201148
}
11211149

1150+
public Task DisconnectNode(EndPoint endPoint, CancellationToken cancellationToken = default)
1151+
=> SendCommandAsync(RPCOperations.disconnectnode, cancellationToken, new object[] { endPoint.ToString() });
1152+
public Task DisconnectNode(int peerId, CancellationToken cancellationToken = default)
1153+
=> SendCommandAsync(RPCOperations.disconnectnode, cancellationToken, new object[] { "", peerId });
1154+
11221155
public void AddNode(EndPoint nodeEndPoint, bool onetry = false)
11231156
{
11241157
if (nodeEndPoint == null)
@@ -2435,6 +2468,7 @@ public NodeServices Services
24352468
{
24362469
get; internal set;
24372470
}
2471+
public string[] ServicesNames { get; set; }
24382472
public DateTimeOffset LastSend
24392473
{
24402474
get; internal set;

NBitcoin/RPC/RPCOperations.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,7 @@ public enum RPCOperations
123123
unloadwallet,
124124
addpeeraddress,
125125
savemempool,
126+
getblockfrompeer,
127+
disconnectnode,
126128
}
127129
}

NBitcoin/Transaction.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ public static TxIn CreateCoinbase(int height)
340340
return txin;
341341
}
342342

343+
/// <summary>
344+
/// Remove <see cref="ScriptSig"/> and <see cref="WitScript"/> from this input.
345+
/// </summary>
343346
public void RemoveSignatures()
344347
{
345348
ScriptSig = Script.Empty;
@@ -1450,7 +1453,9 @@ public static Transaction Parse(string hex, Network network)
14501453
throw new ArgumentNullException(nameof(network));
14511454
return Load(Encoders.Hex.DecodeData(hex), network);
14521455
}
1453-
1456+
/// <summary>
1457+
/// Remove <see cref="TxIn.ScriptSig"/> and <see cref="TxIn.WitScript"/> from all inputs.
1458+
/// </summary>
14541459
public void RemoveSignatures()
14551460
{
14561461
foreach (var input in Inputs)

0 commit comments

Comments
 (0)