public void CanGetTransactionsFromMemPool() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); node.ConfigParameters.Add("whitelist", "127.0.0.1"); node.Start(); var rpc = node.CreateRPCClient(); rpc.Generate(101); rpc.SendToAddress(new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest), Money.Coins(1.0m)); var client = node.CreateNodeClient(); client.VersionHandshake(); var transactions = client.GetMempoolTransactions(); Assert.True(transactions.Length == 1); } }
public void CanParseBlock() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); builder.StartAll(); var rpc = node.CreateRPCClient(); rpc.Generate(10); var hash = rpc.GetBestBlockHash(); var b = rpc.GetBlock(hash); Assert.NotNull(b); Assert.Equal(hash, b.GetHash()); new ConcurrentChain(builder.Network); } }
public void CanGetBlockHeader() { using (var builder = NodeBuilderEx.Create()) { var client = builder.CreateNode().CreateRESTClient(); var rpc = builder.Nodes[0].CreateRPCClient(); builder.StartAll(); rpc.Generate(2); var result = client.GetBlockHeadersAsync(RegNetGenesisBlock.GetHash(), 3).Result; var headers = result.ToArray(); var last = headers.Last(); Assert.Equal(3, headers.Length); Assert.Equal(rpc.GetBestBlockHash(), last.GetHash()); Assert.Equal(headers[1].GetHash(), last.HashPrevBlock); Assert.Equal(RegNetGenesisBlock.GetHash(), headers[1].HashPrevBlock); } }
public void CanCalculateChainWork() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); var client = node.CreateRESTClient(); var rpc = node.CreateRPCClient(); builder.StartAll(); var info = client.GetChainInfoAsync().Result; Assert.Equal("regtest", info.Chain); Assert.Equal(new ChainedBlock(Network.RegTest.GetGenesis().Header, 0).GetChainWork(false), info.ChainWork); rpc.Generate(10); var chain = node.CreateNodeClient().GetChain(); info = client.GetChainInfoAsync().Result; Assert.Equal(info.ChainWork, chain.Tip.GetChainWork(false)); } }
public void ThrowsRestApiClientException() { using (var builder = NodeBuilderEx.Create()) { var client = builder.CreateNode().CreateRESTClient(); builder.StartAll(); var unexistingBlockId = uint256.Parse("100000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820"); Assert.Throws <RestApiException>(() => client.GetBlock(unexistingBlockId)); var txId = uint256.Parse("7569ce92f93f9afd51ffae243e04076be4e5088cf69501aab6de9ede5c331402"); Assert.Throws <RestApiException>(() => client.GetTransaction(txId)); var result = client.GetBlockHeaders(unexistingBlockId, 3); var headers = result.ToArray(); Assert.Empty(headers); } }
public void HasCorrectGenesisBlock() { using (var builder = NodeBuilderEx.Create()) { var rpc = builder.CreateNode().CreateRPCClient(); builder.StartAll(); var genesis = rpc.GetBlock(0); if (builder.Network == Altcoins.Liquid.Instance.Regtest) { Assert.Contains(genesis.Transactions.SelectMany(t => t.Outputs).OfType <ElementsTxOut>(), o => o.IsPeggedAsset == true && o.ConfidentialValue.Amount != null && o.ConfidentialValue.Amount != Money.Zero); } var actual = genesis.GetHash(); var calculatedGenesis = builder.Network.GetGenesis().GetHash(); Assert.Equal(calculatedGenesis, actual); Assert.Equal(rpc.GetBlockHash(0), calculatedGenesis); } }
public void CanGetMemPool() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); var rpc = node.CreateRPCClient(); node.ConfigParameters.Add("whitelist", "127.0.0.1"); node.Start(); rpc.Generate(102); for (int i = 0; i < 2; i++) { node.CreateRPCClient().SendToAddress(new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest), Money.Coins(1.0m)); } var client = node.CreateNodeClient(); var txIds = client.GetMempool(); Assert.True(txIds.Length == 2); } }
public void CanSyncWithPoW() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); builder.StartAll(); node.Generate(100); var nodeClient = node.CreateNodeClient(); nodeClient.VersionHandshake(); ConcurrentChain chain = new ConcurrentChain(builder.Network); nodeClient.SynchronizeChain(chain, new Protocol.SynchronizeChainOptions() { SkipPoWCheck = false }); Assert.Equal(100, chain.Height); } }
public void CanGetBlocksWithProtocol() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(true); var rpc = node.CreateRPCClient(); rpc.Generate(50); var client = node.CreateNodeClient(); var chain = client.GetChain(); var blocks = client.GetBlocks(chain.GetBlock(20).HashBlock).ToArray(); Assert.Equal(20, blocks.Length); Assert.Equal(chain.GetBlock(20).HashBlock, blocks.Last().Header.GetHash()); blocks = client.GetBlocksFromFork(chain.GetBlock(45)).ToArray(); Assert.Equal(5, blocks.Length); Assert.Equal(chain.GetBlock(50).HashBlock, blocks.Last().Header.GetHash()); Assert.Equal(chain.GetBlock(46).HashBlock, blocks.First().Header.GetHash()); } }
public void CanGetChainsConcurrenty() { using (var builder = NodeBuilderEx.Create()) { bool generating = true; var node = builder.CreateNode(true); var rpc = node.CreateRPCClient(); Task.Run(() => { rpc.Generate(600); generating = false; }); var nodeClient = node.CreateNodeClient(); nodeClient.PollHeaderDelay = TimeSpan.FromSeconds(2); nodeClient.VersionHandshake(); Random rand = new Random(); Thread.Sleep(1000); var chains = Enumerable.Range(0, 5) .Select(_ => Task.Factory.StartNew(() => { Thread.Sleep(rand.Next(0, 1000)); return(nodeClient.GetChain()); })) .Select(t => t.Result) .ToArray(); while (generating) { SyncAll(nodeClient, rand, chains); } SyncAll(nodeClient, rand, chains); foreach (var c in chains) { Assert.Equal(600, c.Height); } var chainNoHeader = nodeClient.GetChain(new SynchronizeChainOptions() { SkipPoWCheck = true, StripHeaders = true }); Assert.False(chainNoHeader.Tip.HasHeader); } }
public void CanCancelConnection() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(true); CancellationTokenSource cts = new CancellationTokenSource(); cts.Cancel(); try { var client = Node.Connect(Network.RegTest, "127.0.0.1:" + node.ProtocolPort.ToString(), new NodeConnectionParameters() { ConnectCancellation = cts.Token }); Assert.False(true, "Should have thrown"); } catch (OperationCanceledException) { } } }
public void SynchronizeChainSurviveReorg() { using (var builder = NodeBuilderEx.Create()) { ConcurrentChain chain = new ConcurrentChain(Network.RegTest); var node1 = builder.CreateNode(true); node1.Generate(10); node1.CreateNodeClient().SynchronizeChain(chain); Assert.Equal(10, chain.Height); var node2 = builder.CreateNode(true); node2.Generate(12); var node2c = node2.CreateNodeClient(); node2c.PollHeaderDelay = TimeSpan.FromSeconds(2); node2c.SynchronizeChain(chain); Assert.Equal(12, chain.Height); } }
public void DoesRPCCapabilitiesWellAdvertised() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); builder.StartAll(); node.Generate(builder.Network.Consensus.CoinbaseMaturity + 1); var rpc = node.CreateRPCClient(); rpc.ScanRPCCapabilities(); Assert.NotNull(rpc.Capabilities); CheckCapabilities(rpc, "getnetworkinfo", rpc.Capabilities.SupportGetNetworkInfo); CheckCapabilities(rpc, "scantxoutset", rpc.Capabilities.SupportScanUTXOSet); CheckCapabilities(rpc, "signrawtransactionwithkey", rpc.Capabilities.SupportSignRawTransactionWith); CheckCapabilities(rpc, "estimatesmartfee", rpc.Capabilities.SupportEstimateSmartFee); try { var address = rpc.GetNewAddress(new GetNewAddressRequest() { AddressType = AddressType.Bech32 }); // If this fail, rpc support segwit bug you said it does not Assert.Equal(rpc.Capabilities.SupportSegwit, address.ScriptPubKey.IsScriptType(ScriptType.Witness)); if (rpc.Capabilities.SupportSegwit) { Assert.True(builder.Network.Consensus.SupportSegwit, "The node RPC support segwit, but Network.Consensus.SupportSegwit is set to false"); rpc.SendToAddress(address, Money.Coins(1.0m)); } else { Assert.False(builder.Network.Consensus.SupportSegwit, "The node RPC does not support segwit, but Network.Consensus.SupportSegwit is set to true (This error can be normal if you are using a old node version)"); } } catch (RPCException) when(!rpc.Capabilities.SupportSegwit) { } } }
public void CanCalculateTransactionHash() { using (var builder = NodeBuilderEx.Create()) { var rpc = builder.CreateNode().CreateRPCClient(); builder.StartAll(); var blockHash = rpc.Generate(1)[0]; var block = rpc.GetBlock(blockHash); Transaction walletTx = null; try { walletTx = rpc.GetRawTransaction(block.Transactions[0].GetHash(), block.GetHash()); } // Some nodes does not support the blockid catch { walletTx = rpc.GetRawTransaction(block.Transactions[0].GetHash()); } Assert.Equal(walletTx.ToHex(), block.Transactions[0].ToHex()); } }
public void CanParseAddress() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); builder.StartAll(); var addr = node.CreateRPCClient().SendCommand(RPC.RPCOperations.getnewaddress).Result.ToString(); var addr2 = BitcoinAddress.Create(addr, builder.Network).ToString(); Assert.Equal(addr, addr2); var address = (BitcoinAddress) new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, builder.Network); // Test normal address var isValid = ((JObject)node.CreateRPCClient().SendCommand("validateaddress", address.ToString()).Result)["isvalid"].Value <bool>(); Assert.True(isValid); // Test p2sh address = new Key().PubKey.ScriptPubKey.Hash.ScriptPubKey.GetDestinationAddress(builder.Network); isValid = ((JObject)node.CreateRPCClient().SendCommand("validateaddress", address.ToString()).Result)["isvalid"].Value <bool>(); Assert.True(isValid); } }
public async Task CanMaskExceptionThrownByMessageReceivers() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(); var rpc = node.CreateRPCClient(); node.Start(); var nodeClient = node.CreateNodeClient(); TaskCompletionSource <bool> ok = new TaskCompletionSource <bool>(); nodeClient.VersionHandshake(); nodeClient.UncaughtException += (s, m) => { ok.TrySetResult(m.GetType() == typeof(Exception) && m.Message == "test"); }; nodeClient.MessageReceived += (s, m) => { throw new Exception("test"); }; nodeClient.SendMessage(new PingPayload()); Assert.True(await ok.Task); } }
public void CanMaintainChainWithChainBehavior() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(true).CreateNodeClient(); builder.Nodes[0].Generate(600); var rpc = builder.Nodes[0].CreateRPCClient(); var chain = node.GetChain(rpc.GetBlockHash(500)); Assert.True(chain.Height == 500); using (var tester = new NodeServerTester()) { var n1 = tester.Node1; n1.Behaviors.Add(new ChainBehavior(chain)); n1.VersionHandshake(); Assert.True(n1.MyVersion.StartHeight == 500); var n2 = tester.Node2; Assert.True(n2.MyVersion.StartHeight == 0); Assert.True(n2.PeerVersion.StartHeight == 500); Assert.True(n1.State == NodeState.HandShaked); Assert.True(n2.State == NodeState.HandShaked); var behavior = new ChainBehavior(new ConcurrentChain(Network.RegTest)); n2.Behaviors.Add(behavior); TestUtils.Eventually(() => behavior.Chain.Height == 500); var chain2 = n2.GetChain(rpc.GetBlockHash(500)); Assert.True(chain2.Height == 500); var chain1 = n1.GetChain(rpc.GetBlockHash(500)); Assert.True(chain1.Height == 500); chain1 = n1.GetChain(rpc.GetBlockHash(499)); Assert.True(chain1.Height == 499); //Should not broadcast above HighestValidatorPoW n1.Behaviors.Find <ChainBehavior>().SharedState.HighestValidatedPoW = chain1.GetBlock(300); chain1 = n2.GetChain(rpc.GetBlockHash(499)); Assert.True(chain1.Height == 300); } } }
public void CanHandshakeWithSeveralTemplateBehaviors() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(true); node.Generate(101); AddressManager manager = new AddressManager(); manager.Add(new NetworkAddress(node.NodeEndpoint), IPAddress.Loopback); var chain = new SlimChain(builder.Network.GenesisHash); NodesGroup group = new NodesGroup(builder.Network, new NodeConnectionParameters() { Services = NodeServices.Nothing, IsRelay = true, TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None }, new SlimChainBehavior(chain), new PingPongBehavior() } }); group.AllowSameGroup = true; group.MaximumNodeConnection = 1; var connecting = WaitConnected(group); try { group.Connect(); connecting.GetAwaiter().GetResult(); Eventually(() => { Assert.Equal(101, chain.Height); }); var ms = new MemoryStream(); chain.Save(ms); var chain2 = new SlimChain(chain.Genesis); ms.Position = 0; chain2.Load(ms); Assert.Equal(chain.Tip, chain2.Tip); using (var fs = new FileStream("test.slim.dat", FileMode.Create, FileAccess.Write, FileShare.None, 1024 * 1024)) { chain.Save(fs); fs.Flush(); } chain.ResetToGenesis(); using (var fs = new FileStream("test.slim.dat", FileMode.Open, FileAccess.Read, FileShare.None, 1024 * 1024)) { chain.Load(fs); } Assert.Equal(101, chain2.Height); chain.ResetToGenesis(); } finally { group.Disconnect(); } } }
public void CanSignPSBTWithRootAndAccountKey() { using (var nodeBuilder = NodeBuilderEx.Create()) { var rpc = nodeBuilder.CreateNode().CreateRPCClient(); nodeBuilder.StartAll(); rpc.Generate(102); uint hardenedFlag = 0U; retry: var masterKey = new ExtKey(); var accountKeyPath = new RootedKeyPath(masterKey, new KeyPath("49'/0'/0'")); var accountKey = masterKey.Derive(accountKeyPath); var addresses = Enumerable.Range(0, 5) .Select(i => { var addressPath = new KeyPath(new uint[] { 0U | hardenedFlag, (uint)i | hardenedFlag }); var fullAddressPath = accountKeyPath.Derive(addressPath); var address = accountKey.Derive(addressPath).GetPublicKey().WitHash.GetAddress(nodeBuilder.Network); return(new { FullAddressPath = fullAddressPath, AddressPath = addressPath, Address = address }); }).ToList(); var changeAddress = addresses.Last(); addresses = addresses.Take(addresses.Count - 1).ToList(); // Fund the addresses var coins = addresses.Select(async kra => { var id = await rpc.SendToAddressAsync(kra.Address, Money.Coins(1)); var tx = await rpc.GetRawTransactionAsync(id); return(tx.Outputs.AsCoins().Where(o => o.ScriptPubKey == kra.Address.ScriptPubKey).Single()); }).Select(t => t.Result).ToArray(); var destination = new Key().ScriptPubKey; var amount = new Money(1, MoneyUnit.BTC); var builder = Network.Main.CreateTransactionBuilder(); var fee = new Money(100_000L); var partiallySignedTx = builder .AddCoins(coins) .Send(destination, amount) .SetChange(changeAddress.Address) .SendFees(fee) .BuildPSBT(false); partiallySignedTx.AddKeyPath(masterKey, addresses.Concat(new[] { changeAddress }).Select(a => a.FullAddressPath.KeyPath).ToArray()); var expectedBalance = -amount - fee; var actualBalance = partiallySignedTx.GetBalance(ScriptPubKeyType.Segwit, accountKey, accountKeyPath); Assert.Equal(expectedBalance, actualBalance); actualBalance = partiallySignedTx.GetBalance(ScriptPubKeyType.Segwit, masterKey); Assert.Equal(expectedBalance, actualBalance); Assert.Equal(Money.Zero, partiallySignedTx.GetBalance(ScriptPubKeyType.Legacy, masterKey)); // You can sign with accountKey and keypath var memento = partiallySignedTx.Clone(); partiallySignedTx.SignAll(ScriptPubKeyType.Segwit, accountKey, accountKeyPath); Assert.True(partiallySignedTx.Inputs.All(i => i.PartialSigs.Count == 1)); partiallySignedTx.Finalize(); var partiallySignedTx2 = memento; // Or you can sign with the masterKey partiallySignedTx2.SignAll(ScriptPubKeyType.Segwit, masterKey); Assert.True(partiallySignedTx2.Inputs.All(i => i.PartialSigs.Count == 1)); partiallySignedTx2.Finalize(); Assert.Equal(partiallySignedTx, partiallySignedTx2); var signedTx = partiallySignedTx.ExtractTransaction(); rpc.SendRawTransaction(signedTx); var errors = builder.Check(signedTx); Assert.Empty(errors); if (hardenedFlag == 0) { hardenedFlag = 0x80000000; goto retry; } } }
public void CanBuildSegwitP2SHMultisigTransactionsWithPSBT() { using (var nodeBuilder = NodeBuilderEx.Create()) { var rpc = nodeBuilder.CreateNode().CreateRPCClient(); nodeBuilder.StartAll(); rpc.Generate(102); // Build the keys and addresses var masterKeys = Enumerable.Range(0, 3).Select(_ => new ExtKey()).ToArray(); var keyRedeemAddresses = Enumerable.Range(0, 4) .Select(i => masterKeys.Select(m => m.Derive(i, false)).ToArray()) .Select(keys => ( Keys: keys.Select(k => k.PrivateKey).ToArray(), Redeem: PayToMultiSigTemplate.Instance.GenerateScriptPubKey(keys.Length, keys.Select(k => k.PrivateKey.PubKey).ToArray())) ).Select(_ => ( Keys: _.Keys, Redeem: _.Redeem, Address: _.Redeem.WitHash.ScriptPubKey.Hash.ScriptPubKey.GetDestinationAddress(nodeBuilder.Network) )); // Fund the addresses var scriptCoins = keyRedeemAddresses.Select(async kra => { var id = await rpc.SendToAddressAsync(kra.Address, Money.Coins(1)); var tx = await rpc.GetRawTransactionAsync(id); return(tx.Outputs.AsCoins().Where(o => o.ScriptPubKey == kra.Address.ScriptPubKey) .Select(c => c.ToScriptCoin(kra.Redeem)).Single()); }).Select(t => t.Result).ToArray(); var destination = new Key().ScriptPubKey; var amount = new Money(1, MoneyUnit.BTC); var redeemScripts = keyRedeemAddresses.Select(kra => kra.Redeem).ToArray(); var privateKeys = keyRedeemAddresses.SelectMany(kra => kra.Keys).ToArray(); var builder = Network.Main.CreateTransactionBuilder(); var rate = new FeeRate(Money.Satoshis(1), 1); var partiallySignedTx = builder .AddCoins(scriptCoins) .AddKeys(privateKeys[0]) .Send(destination, amount) .SubtractFees() .SetChange(new Key().ScriptPubKey) .SendEstimatedFees(rate) .BuildPSBT(true); Assert.True(partiallySignedTx.Inputs.All(i => i.PartialSigs.Count == 1)); partiallySignedTx = PSBT.Load(partiallySignedTx.ToBytes(), Network.Main); Network.Main.CreateTransactionBuilder() .AddKeys(privateKeys[1], privateKeys[2]) .SignPSBT(partiallySignedTx); Assert.True(partiallySignedTx.Inputs.All(i => i.PartialSigs.Count == 3)); partiallySignedTx.Finalize(); var signedTx = partiallySignedTx.ExtractTransaction(); rpc.SendRawTransaction(signedTx); var errors = builder.Check(signedTx); Assert.Empty(errors); } }
public void CanGetMerkleRoot() { using (var builder = NodeBuilderEx.Create()) { var node = builder.CreateNode(true); var rpc = node.CreateRPCClient(); var nodeClient = node.CreateNodeClient(); rpc.Generate(101); List <TxDestination> knownAddresses = new List <TxDestination>(); var batch = rpc.PrepareBatch(); for (int i = 0; i < 20; i++) { var address = (BitcoinPubKeyAddress) new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, rpc.Network); knownAddresses.Add(address.Hash); #pragma warning disable CS4014 batch.SendToAddressAsync(address, Money.Coins(0.5m)); #pragma warning restore CS4014 } batch.SendBatch(); knownAddresses = knownAddresses.Take(10).ToList(); var blockId = rpc.Generate(1)[0]; var block = rpc.GetBlock(blockId); Assert.Equal(21, block.Transactions.Count); var knownTx = block.Transactions[1].GetHash(); nodeClient.VersionHandshake(); using (var list = nodeClient.CreateListener() .Where(m => m.Message.Payload is MerkleBlockPayload || m.Message.Payload is TxPayload)) { BloomFilter filter = new BloomFilter(1, 0.0001, 50, BloomFlags.UPDATE_NONE); foreach (var a in knownAddresses) { filter.Insert(a.ToBytes()); } nodeClient.SendMessageAsync(new FilterLoadPayload(filter)); nodeClient.SendMessageAsync(new GetDataPayload(new InventoryVector(InventoryType.MSG_FILTERED_BLOCK, block.GetHash()))); var merkle = list.ReceivePayload <MerkleBlockPayload>(); var tree = merkle.Object.PartialMerkleTree; Assert.True(tree.Check(block.Header.HashMerkleRoot)); Assert.True(tree.GetMatchedTransactions().Count() >= 10); Assert.Contains(knownTx, tree.GetMatchedTransactions()); List <Transaction> matched = new List <Transaction>(); for (int i = 0; i < tree.GetMatchedTransactions().Count(); i++) { matched.Add(list.ReceivePayload <TxPayload>().Object); } Assert.True(matched.Count >= 10); tree = tree.Trim(knownTx); Assert.True(tree.GetMatchedTransactions().Count() == 1); Assert.Contains(knownTx, tree.GetMatchedTransactions()); Action act = () => { foreach (var match in matched) { Assert.True(filter.IsRelevantAndUpdate(match)); } }; act(); filter = filter.Clone(); act(); var unknownBlock = uint256.Parse("00000000ad262227291eaf90cafdc56a8f8451e2d7653843122c5bb0bf2dfcdd"); nodeClient.SendMessageAsync(new GetDataPayload(new InventoryVector(InventoryType.MSG_FILTERED_BLOCK, Network.RegTest.GetGenesis().GetHash()))); merkle = list.ReceivePayload <MerkleBlockPayload>(); tree = merkle.Object.PartialMerkleTree; Assert.True(tree.Check(merkle.Object.Header.HashMerkleRoot)); Assert.True(!tree.GetMatchedTransactions().Contains(knownTx)); } } }