static internal WalletCreation FromJson(JObject obj) { WalletCreation creation = new WalletCreation(); creation.Network = null; JToken unused; if (obj.TryGetValue("Name", out unused)) { creation.Name = (string)obj["Name"]; } else { creation.Name = null; } if (obj.Property("PurgeConnectionOnFilterChange") != null) { creation.PurgeConnectionOnFilterChange = (bool)obj["PurgeConnectionOnFilterChange"]; } JToken network; if (obj.TryGetValue("Network", out network)) { creation.Network = Network.GetNetwork((string)network); } creation.SignatureRequired = (int)(long)obj["SignatureRequired"]; creation.DerivationPath = KeyPath.Parse((string)obj["DerivationPath"]); creation.UseP2SH = (bool)obj["UseP2SH"]; var array = (JArray)obj["RootKeys"]; var keys = array.Select(i => new BitcoinExtPubKey((string)i)).ToArray(); creation.Network = creation.Network ?? keys[0].Network; creation.RootKeys = keys.Select(k => k.ExtPubKey).ToArray(); return(creation); }
void LoadCore(Stream stream) { JObject obj = JObject.Load(new JsonTextReader(new StreamReader(stream)) { DateParseHandling = DateParseHandling.DateTimeOffset }); _Parameters = WalletCreation.FromJson((JObject)obj["Parameters"]); _PathStates = new Dictionary <KeyPath, PathState>(); if (obj.Property("CurrentIndex") != null) //legacy { var idx = (int)(long)obj["CurrentIndex"]; var loadedKeys = (int)(long)obj["LoadedKeys"]; _PathStates.Add(_Parameters.DerivationPath.Derive(0), new PathState() { Next = idx, Loaded = loadedKeys }); _PathStates.Add(_Parameters.DerivationPath.Derive(1), new PathState() { Next = idx, Loaded = loadedKeys }); } var indices = obj["Indices"] as JArray; if (indices != null) { foreach (var indice in indices.OfType <JObject>()) { _PathStates.Add(KeyPath.Parse((string)indice["KeyPath"]), new PathState() { Next = (int)(long)indice["Next"], Loaded = (int)(long)indice["Loaded"] }); } } _KeyPoolSize = (int)(long)obj["KeyPoolSize"]; Created = (DateTimeOffset)obj["Created"]; _ScanLocation = new BlockLocator(); _ScanLocation.FromBytes(Encoders.Hex.DecodeData((string)obj["Location"])); _KnownScripts.Clear(); var knownScripts = (JArray)obj["KnownScripts"]; foreach (var known in knownScripts.OfType <JObject>()) { Script script = Script.FromBytesUnsafe(Encoders.Hex.DecodeData((string)known["ScriptPubKey"])); if (known["KeyPath"] != null) //Legacy data { KeyPath keypath = KeyPath.Parse((string)known["KeyPath"]); _KnownScripts.Add(script, _Parameters.DerivationPath.Derive(keypath)); } if (known["AbsoluteKeyPath"] != null) { KeyPath keypath = KeyPath.Parse((string)known["AbsoluteKeyPath"]); _KnownScripts.Add(script, keypath); } } }
/// <summary> /// Create a new wallet /// </summary> /// <param name="creation">Creation parameters</param> /// <param name="keyPoolSize">The number of keys which will be pre-created</param> public Wallet(WalletCreation creation, int keyPoolSize = 500) { if (creation == null) { throw new ArgumentNullException("creation"); } _Parameters = creation; _ScanLocation = new BlockLocator(); _ScanLocation.Blocks.Add(creation.Network.GetGenesis().GetHash()); _KeyPoolSize = keyPoolSize; Created = DateTimeOffset.UtcNow; }
#pragma warning disable CS0612 // Type or member is obsolete /// <summary> /// Create a new wallet /// </summary> /// <param name="creation">Creation parameters</param> /// <param name="keyPoolSize">The number of keys which will be pre-created</param> public Wallet(WalletCreation creation, int keyPoolSize = 500) #pragma warning restore CS0612 // Type or member is obsolete { if (creation == null) { throw new ArgumentNullException(nameof(creation)); } _Parameters = creation; _ScanLocation = new BlockLocator(); _ScanLocation.Blocks.Add(creation.Network.GetGenesis().GetHash()); _KeyPoolSize = keyPoolSize; Created = DateTimeOffset.UtcNow; }
static internal WalletCreation FromJson(JObject obj) { WalletCreation creation = new WalletCreation(); creation.SignatureRequired = (int)(long)obj["SignatureRequired"]; creation.DerivationPath = KeyPath.Parse((string)obj["DerivationPath"]); creation.UseP2SH = (bool)obj["UseP2SH"]; var array = (JArray)obj["RootKeys"]; var keys = array.Select(i => new BitcoinExtPubKey((string)i)).ToArray(); creation.Network = keys[0].Network; creation.RootKeys = keys.Select(k => k.ExtPubKey).ToArray(); return(creation); }
void LoadCore(Stream stream) { JObject obj = JObject.Load(new JsonTextReader(new StreamReader(stream)) { DateParseHandling = DateParseHandling.DateTimeOffset }); _CurrentIndex = (int)(long)obj["CurrentIndex"]; _KeyPoolSize = (int)(long)obj["KeyPoolSize"]; _LoadedKeys = (int)(long)obj["LoadedKeys"]; Created = (DateTimeOffset)obj["Created"]; _ScanLocation = new BlockLocator(); _ScanLocation.FromBytes(Encoders.Hex.DecodeData((string)obj["Location"])); _Parameters = WalletCreation.FromJson((JObject)obj["Parameters"]); _KnownScripts.Clear(); var knownScripts = (JArray)obj["KnownScripts"]; foreach (var known in knownScripts.OfType <JObject>()) { Script script = Script.FromBytesUnsafe(Encoders.Hex.DecodeData((string)known["ScriptPubKey"])); KeyPath keypath = KeyPath.Parse((string)known["KeyPath"]); _KnownScripts.Add(script, keypath); } }
public void CanSyncWalletCore(WalletCreation creation) { using(NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var notifiedTransactions = new List<WalletTransaction>(); var chainBuilder = new BlockchainBuilder(); SetupSPVBehavior(servers, chainBuilder); NodesGroup connected = CreateGroup(servers, 1); Wallet wallet = new Wallet(creation, keyPoolSize: 11); wallet.NewWalletTransaction += (s, a) => notifiedTransactions.Add(a); Assert.True(wallet.State == WalletState.Created); wallet.Configure(connected); wallet.Connect(); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.Chain.Height == 1); for(int i = 0 ; i < 9 ; i++) { wallet.GetNextScriptPubKey(); } wallet.GetNextScriptPubKey(); //Should provoke purge TestUtils.Eventually(() => wallet.State == WalletState.Disconnected && wallet.ConnectedNodes == 0); TestUtils.Eventually(() => wallet.ConnectedNodes == 1); TestUtils.Eventually(() => servers.Server1.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find<SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); var k = wallet.GetNextScriptPubKey(); Assert.NotNull(wallet.GetKeyPath(k)); if(creation.UseP2SH) { var p2sh = k.GetDestinationAddress(Network.TestNet) as BitcoinScriptAddress; Assert.NotNull(p2sh); var redeem = wallet.GetRedeemScript(p2sh); Assert.NotNull(redeem); Assert.Equal(redeem.Hash, p2sh.Hash); } Assert.Equal(creation.UseP2SH, k.GetDestinationAddress(Network.TestNet) is BitcoinScriptAddress); chainBuilder.GiveMoney(k, Money.Coins(1.0m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1); Assert.Equal(1, notifiedTransactions.Count); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Where(t => t.BlockInformation != null).Count() == 1); Assert.Equal(2, notifiedTransactions.Count); chainBuilder.Broadcast = false; chainBuilder.GiveMoney(k, Money.Coins(1.5m)); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2); Assert.Equal(3, notifiedTransactions.Count); chainBuilder.Broadcast = false; for(int i = 0 ; i < 30 ; i++) { chainBuilder.FindBlock(); } chainBuilder.GiveMoney(k, Money.Coins(0.001m)); chainBuilder.FindBlock(); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); //Sync automatically TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 3); //Save and restore wallet MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); ////// //Save and restore tracker ms = new MemoryStream(); var tracker = connected.NodeConnectionParameters.TemplateBehaviors.Find<TrackerBehavior>(); connected.NodeConnectionParameters.TemplateBehaviors.Remove(tracker); tracker.Tracker.Save(ms); ms.Position = 0; tracker = new TrackerBehavior(Tracker.Load(ms), wallet.Chain); connected.NodeConnectionParameters.TemplateBehaviors.Add(tracker); ////// wallet2.Configure(connected); wallet2.Connect(); Assert.Equal(wallet.Created, wallet2.Created); Assert.Equal(wallet.GetNextScriptPubKey(), wallet2.GetNextScriptPubKey()); Assert.True(wallet.GetKnownScripts().Length == wallet2.GetKnownScripts().Length); TestUtils.Eventually(() => wallet2.GetTransactions().Summary.Confirmed.TransactionCount == 3); var fork = wallet.Chain.FindFork(wallet2._ScanLocation); Assert.True(fork.Height == chainBuilder.Chain.Height); } }
public void CanSyncWalletCore(WalletCreation creation) { using(NodeServerTester servers = new NodeServerTester(Network.TestNet)) { var chainBuilder = new BlockchainBuilder(); //Simulate SPV compatible server servers.Server1.InboundNodeConnectionParameters.Services = NodeServices.Network; servers.Server1.InboundNodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(chainBuilder.Chain) { AutoSync = false }); servers.Server1.InboundNodeConnectionParameters.TemplateBehaviors.Add(new SPVBehavior(chainBuilder)); ///////////// //The SPV client does not verify the chain and keep one connection alive with Server1 NodeConnectionParameters parameters = new NodeConnectionParameters(); Wallet.ConfigureDefaultNodeConnectionParameters(parameters); parameters.IsTrusted = true; AddressManagerBehavior addrman = new AddressManagerBehavior(new AddressManager()); addrman.AddressManager.Add(new NetworkAddress(servers.Server1.ExternalEndpoint), IPAddress.Parse("127.0.0.1")); parameters.TemplateBehaviors.Add(addrman); NodesGroup connected = new NodesGroup(Network.TestNet, parameters); connected.AllowSameGroup = true; connected.MaximumNodeConnection = 1; ///////////// Wallet wallet = new Wallet(creation, keyPoolSize: 11); Assert.True(wallet.State == WalletState.Created); wallet.Connect(connected); Assert.True(wallet.State == WalletState.Disconnected); TestUtils.Eventually(() => connected.ConnectedNodes.Count == 1); Assert.True(wallet.State == WalletState.Connected); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.Chain.Height == 1); for(int i = 0 ; i < 9 ; i++) { wallet.GetNextScriptPubKey(); } wallet.GetNextScriptPubKey(); //Should provoke purge TestUtils.Eventually(() => wallet.State == WalletState.Disconnected && wallet.ConnectedNodes == 0); TestUtils.Eventually(() => wallet.ConnectedNodes == 1); TestUtils.Eventually(() => servers.Server1.ConnectedNodes.Count == 1); var spv = servers.Server1.ConnectedNodes.First().Behaviors.Find<SPVBehavior>(); TestUtils.Eventually(() => spv._Filter != null); var k = wallet.GetNextScriptPubKey(); Assert.Equal(creation.UseP2SH, k.GetDestinationAddress(Network.TestNet) is BitcoinScriptAddress); chainBuilder.GiveMoney(k, Money.Coins(1.0m)); TestUtils.Eventually(() => wallet.GetTransactions().Count == 1); chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Where(t => t.BlockInformation != null).Count() == 1); chainBuilder.Broadcast = false; chainBuilder.GiveMoney(k, Money.Coins(1.5m)); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 2); chainBuilder.Broadcast = false; for(int i = 0 ; i < 30 ; i++) { chainBuilder.FindBlock(); } chainBuilder.GiveMoney(k, Money.Coins(0.001m)); chainBuilder.FindBlock(); chainBuilder.Broadcast = true; chainBuilder.FindBlock(); //Sync automatically TestUtils.Eventually(() => wallet.GetTransactions().Summary.Confirmed.TransactionCount == 3); MemoryStream ms = new MemoryStream(); wallet.Save(ms); ms.Position = 0; var wallet2 = Wallet.Load(ms); wallet2.Connect(connected); Assert.Equal(wallet.Created, wallet2.Created); Assert.Equal(wallet.GetNextScriptPubKey(), wallet2.GetNextScriptPubKey()); Assert.True(wallet.GetKnownScripts().Length == wallet2.GetKnownScripts().Length); var fork = wallet.Chain.FindFork(wallet2._ScanLocation); Assert.True(fork.Height == chainBuilder.Chain.Height - 5); } }
void LoadCore(Stream stream) { JObject obj = JObject.Load(new JsonTextReader(new StreamReader(stream)) { DateParseHandling = DateParseHandling.DateTimeOffset }); _Parameters = WalletCreation.FromJson((JObject)obj["Parameters"]); _PathStates = new Dictionary<KeyPath, PathState>(); if(obj.Property("CurrentIndex") != null) //legacy { var idx = (int)(long)obj["CurrentIndex"]; var loadedKeys = (int)(long)obj["LoadedKeys"]; _PathStates.Add(_Parameters.DerivationPath.Derive(0), new PathState() { Next = idx, Loaded = loadedKeys }); _PathStates.Add(_Parameters.DerivationPath.Derive(1), new PathState() { Next = idx, Loaded = loadedKeys }); } var indices = obj["Indices"] as JArray; if(indices != null) { foreach(var indice in indices.OfType<JObject>()) { _PathStates.Add(KeyPath.Parse((string)indice["KeyPath"]), new PathState() { Next = (int)(long)indice["Next"], Loaded = (int)(long)indice["Loaded"] }); } } _KeyPoolSize = (int)(long)obj["KeyPoolSize"]; Created = (DateTimeOffset)obj["Created"]; _ScanLocation = new BlockLocator(); _ScanLocation.FromBytes(Encoders.Hex.DecodeData((string)obj["Location"])); _KnownScripts.Clear(); var knownScripts = (JArray)obj["KnownScripts"]; foreach(var known in knownScripts.OfType<JObject>()) { Script script = Script.FromBytesUnsafe(Encoders.Hex.DecodeData((string)known["ScriptPubKey"])); if(known["KeyPath"] != null) //Legacy data { KeyPath keypath = KeyPath.Parse((string)known["KeyPath"]); _KnownScripts.Add(script, _Parameters.DerivationPath.Derive(keypath)); } if(known["AbsoluteKeyPath"] != null) { KeyPath keypath = KeyPath.Parse((string)known["AbsoluteKeyPath"]); _KnownScripts.Add(script, keypath); } } }
/// <summary> /// Create a new wallet /// </summary> /// <param name="creation">Creation parameters</param> /// <param name="keyPoolSize">The number of keys which will be pre-created</param> public Wallet(WalletCreation creation, int keyPoolSize = 500) { if(creation == null) throw new ArgumentNullException("creation"); _Parameters = creation; _ScanLocation = new BlockLocator(); _ScanLocation.Blocks.Add(creation.Network.GetGenesis().GetHash()); _KeyPoolSize = keyPoolSize; Created = DateTimeOffset.UtcNow; }
static internal WalletCreation FromJson(JObject obj) { WalletCreation creation = new WalletCreation(); JToken unused; if(obj.TryGetValue("Name", out unused)) creation.Name = (string)obj["Name"]; else creation.Name = null; if(obj.Property("PurgeConnectionOnFilterChange") != null) { creation.PurgeConnectionOnFilterChange = (bool)obj["PurgeConnectionOnFilterChange"]; } creation.SignatureRequired = (int)(long)obj["SignatureRequired"]; creation.DerivationPath = KeyPath.Parse((string)obj["DerivationPath"]); creation.UseP2SH = (bool)obj["UseP2SH"]; var array = (JArray)obj["RootKeys"]; var keys = array.Select(i => new BitcoinExtPubKey((string)i)).ToArray(); creation.Network = keys[0].Network; creation.RootKeys = keys.Select(k => k.ExtPubKey).ToArray(); return creation; }
void LoadCore(Stream stream) { JObject obj = JObject.Load(new JsonTextReader(new StreamReader(stream)) { DateParseHandling = DateParseHandling.DateTimeOffset }); _CurrentIndex = (int)(long)obj["CurrentIndex"]; _KeyPoolSize = (int)(long)obj["KeyPoolSize"]; _LoadedKeys = (int)(long)obj["LoadedKeys"]; Created = (DateTimeOffset)obj["Created"]; _ScanLocation = new BlockLocator(); _ScanLocation.FromBytes(Encoders.Hex.DecodeData((string)obj["Location"])); _Parameters = WalletCreation.FromJson((JObject)obj["Parameters"]); _KnownScripts.Clear(); var knownScripts = (JArray)obj["KnownScripts"]; foreach(var known in knownScripts.OfType<JObject>()) { Script script = Script.FromBytesUnsafe(Encoders.Hex.DecodeData((string)known["ScriptPubKey"])); KeyPath keypath = KeyPath.Parse((string)known["KeyPath"]); _KnownScripts.Add(script, keypath); } }
static internal WalletCreation FromJson(JObject obj) { WalletCreation creation = new WalletCreation(); creation.SignatureRequired = (int)(long)obj["SignatureRequired"]; creation.DerivationPath = KeyPath.Parse((string)obj["DerivationPath"]); creation.UseP2SH = (bool)obj["UseP2SH"]; var array = (JArray)obj["RootKeys"]; var keys = array.Select(i => new BitcoinExtPubKey((string)i)).ToArray(); creation.Network = keys[0].Network; creation.RootKeys = keys.Select(k => k.ExtPubKey).ToArray(); return creation; }