2 * Novacoin classes library
3 * Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 using SQLite.Net.Attributes;
22 using SQLite.Net.Interop;
23 using SQLite.Net.Platform.Generic;
31 /// Key storage item and structure
39 [PrimaryKey, AutoIncrement]
40 public int ItemId { get; set; }
45 public byte[] KeyID { get; set; }
50 public byte[] PublicKey { get; set; }
55 public byte[] PrivateKey { get; set; }
58 /// Compressed key flag
60 public bool IsCompressed { get; set; }
63 /// Is this key a part of KeyPool?
66 public bool IsUsed { get; set; }
69 /// Item creation time
72 public int nTime { get; set; }
76 /// Script storage item and structure
78 [Table("ScriptStorage")]
79 class ScriptStorageItem
82 /// Script item number
84 [PrimaryKey, AutoIncrement]
85 public int ItemId { get; set; }
90 public byte[] ScriptID { get; set; }
95 public byte[] ScriptCode { get; set; }
99 /// select count(...) as Count from ... where ...
100 /// select number from ... where ...
104 public int Num { get; set; }
110 public class CKeyStore : IDisposable
112 private bool disposed = false;
113 private object LockObj = new object();
114 private SQLiteConnection dbConn = null;
115 private int nKeyPoolSize = 100;
118 /// Initialize new instance of key store.
120 /// <param name="strDatabasePath">Path to database file.</param>
121 /// <param name="KeyPoolSize">Number of reserved keys.</param>
122 public CKeyStore(string strDatabasePath="KeyStore.db", int KeyPoolSize = 100)
124 bool firstInit = !File.Exists(strDatabasePath);
126 dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), strDatabasePath);
127 nKeyPoolSize = KeyPoolSize;
133 dbConn.CreateTable<KeyStorageItem>(CreateFlags.AutoIncPK);
134 dbConn.CreateTable<ScriptStorageItem>(CreateFlags.AutoIncPK);
136 GenerateKeys(nKeyPoolSize);
146 public void Dispose()
149 GC.SuppressFinalize(this);
152 protected virtual void Dispose(bool disposing)
158 // Free other state (managed objects).
173 /// Generate keys and insert them to key store.
175 /// <param name="n"></param>
176 private void GenerateKeys(int n)
180 dbConn.BeginTransaction();
183 for (int i = 0; i < n; i++)
185 var keyPair = new CKeyPair();
187 var res = dbConn.Insert(new KeyStorageItem()
189 KeyID = keyPair.KeyID,
190 PublicKey = keyPair.PubKey,
191 PrivateKey = keyPair,
192 IsCompressed = keyPair.IsCompressed,
194 nTime = Interop.GetTime()
197 // TODO: Additional initialization
205 /// Insert key data into table
207 /// <param name="keyPair">CKeyPair instance</param>
208 /// <returns>Result</returns>
209 public bool AddKey(CKeyPair keyPair)
213 var res = dbConn.Insert(new KeyStorageItem()
215 KeyID = keyPair.KeyID,
216 PublicKey = keyPair.PubKey,
217 PrivateKey = keyPair,
218 IsCompressed = keyPair.IsCompressed,
220 nTime = Interop.GetTime()
233 /// Check existance of item with provided KeyID
235 /// <param name="scriptID">Hash160 of public key.</param>
236 /// <returns>Checking result</returns>
237 public bool HaveKey(CKeyID keyID)
239 var QueryCount = dbConn.Query<NumQuery>("select count([ItemID]) from [KeyStorage] where [KeyID] = ?", (byte[])keyID);
241 return QueryCount.First().Num == 1;
245 /// Get the key pair object.
247 /// <param name="keyID">Hash of public key.</param>
248 /// <param name="keyPair">Instance of CKeyPair or null.</param>
249 /// <returns>Result</returns>
250 public bool GetKey(CKeyID keyID, out CKeyPair keyPair)
252 var QueryGet = dbConn.Query<KeyStorageItem>("select * from [KeyStorage] where [KeyID] = ?", (byte[])keyID);
254 if (QueryGet.Count() == 1)
256 keyPair = new CKeyPair(QueryGet.First().PrivateKey);
265 /// Add redeem script to script store.
267 /// <param name="script">CScript instance</param>
268 /// <returns>Result</returns>
269 public bool AddScript(CScript script)
273 var res = dbConn.Insert(new ScriptStorageItem()
275 ScriptID = script.ScriptID,
289 /// Check existance of item with provided ScriptID
291 /// <param name="scriptID">Hash160 of script code.</param>
292 /// <returns>Checking result</returns>
293 public bool HaveScript(CScriptID scriptID)
295 var QueryGet = dbConn.Query<NumQuery>("select count([ItemID]) from [ScriptStorage] where [ScriptID] = ?", (byte[])scriptID);
297 return QueryGet.First().Num == 1;
301 /// Get redeem script from database.
303 /// <param name="scriptID">Script ID, evaluated as Hash160(script code).</param>
304 /// <param name="script">Instance of CScript</param>
305 /// <returns>Result</returns>
306 public bool GetScript(CScriptID scriptID, out CScript script)
308 var QueryGet = dbConn.Query<ScriptStorageItem>("select * from [ScriptStorage] where [ScriptID] = ?", (byte[])scriptID);
310 if (QueryGet.Count() == 1)
312 script = new CScript(QueryGet.First().ScriptCode);
322 /// SQLite return type for ReserveKey
326 public int ItemId { get; set; }
327 public byte[] KeyID { get; set; }
331 /// Reserve key from a list of unused keys.
333 /// <param name="nKeyIndex">Internal index of key</param>
334 /// <returns>CKeyID instance</returns>
335 public CKeyID SelectKey(out int nKeyIndex)
337 var QueryGet = dbConn.Query<ReservedKey>("select ItemId, KeyID from [KeyStorage] where not [IsUsed] order by [nTime] asc limit 1");
339 if (QueryGet.Count() == 1)
341 var res = QueryGet.First();
343 nKeyIndex = res.ItemId;
345 return new CKeyID(res.KeyID);
349 // Generate new keys in case if keypool is exhausted.
351 GenerateKeys(nKeyPoolSize);
353 return SelectKey(out nKeyIndex);
358 /// Mark key as used.
360 /// <param name="nIndex">Internal index.</param>
361 public void MarkUsed(int nIndex)
365 dbConn.Execute("update [KeyStorage] set [IsUsed] = true where [ItemId] = ?", nIndex);
370 /// Mark key as unused.
372 /// <param name="nIndex">Internal index.</param>
373 public void MarkUnused(int nIndex)
377 dbConn.Execute("update [KeyStorage] set [IsUsed] = false where [ItemId] = ?", nIndex);
382 /// Regenerate all unused keys.
384 public void ResetPool()
388 dbConn.Execute("delete from [KeyStorage] where not [IsUsed]");
389 GenerateKeys(nKeyPoolSize);
394 /// Timestamp of oldest item in keystore.
396 public int OldestTime
400 var QueryTime = dbConn.Query<NumQuery>("select [nTime] from [KeyStorage] where not [IsUsed] order by [ItemId] asc limit 1");
402 return QueryTime.First().Num;