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;
30 /// Key storage item and structure
38 [PrimaryKey, AutoIncrement]
39 public int ItemId { get; set; }
44 public byte[] KeyID { get; set; }
49 public byte[] PublicKey { get; set; }
54 public byte[] PrivateKey { get; set; }
57 /// Compressed key flag
59 public bool IsCompressed { get; set; }
62 /// Is this key a part of KeyPool?
65 public bool IsUsed { get; set; }
68 /// Item creation time
71 public int nTime { get; set; }
75 /// Script storage item and structure
77 [Table("ScriptStorage")]
78 class ScriptStorageItem
81 /// Script item number
83 [PrimaryKey, AutoIncrement]
84 public int ItemId { get; set; }
89 public byte[] ScriptID { get; set; }
94 public byte[] ScriptCode { get; set; }
98 /// select count(...) as Count from ... where ...
99 /// select number from ... where ...
103 public int Num { get; set; }
109 public class CKeyStore
111 private object LockObj = new object();
112 private SQLiteConnection dbConn = null;
114 private int nKeyPoolSize = 100;
117 /// Initialize new instance of key store.
119 /// <param name="strDatabasePath">Path to database file.</param>
120 /// <param name="KeyPoolSize">Number of reserved keys.</param>
121 public CKeyStore(string strDatabasePath="KeyStore.db", int KeyPoolSize = 100)
123 bool firstInit = !File.Exists(strDatabasePath);
125 dbConn = new SQLiteConnection(new SQLitePlatformGeneric(), strDatabasePath);
126 nKeyPoolSize = KeyPoolSize;
132 dbConn.CreateTable<KeyStorageItem>(CreateFlags.AutoIncPK);
133 dbConn.CreateTable<ScriptStorageItem>(CreateFlags.AutoIncPK);
135 GenerateKeys(nKeyPoolSize);
150 /// Generate keys and insert them to key store.
152 /// <param name="n"></param>
153 private void GenerateKeys(int n)
157 dbConn.BeginTransaction();
160 for (int i = 0; i < n; i++)
162 var keyPair = new CKeyPair();
164 var res = dbConn.Insert(new KeyStorageItem()
166 KeyID = keyPair.KeyID.hashBytes,
167 PublicKey = keyPair.PublicBytes,
168 PrivateKey = keyPair.SecretBytes,
169 IsCompressed = keyPair.IsCompressed,
171 nTime = Interop.GetTime()
174 // TODO: Additional initialization
182 /// Insert key data into table
184 /// <param name="keyPair">CKeyPair instance</param>
185 /// <returns>Result</returns>
186 public bool AddKey(CKeyPair keyPair)
190 var res = dbConn.Insert(new KeyStorageItem()
192 KeyID = keyPair.KeyID.hashBytes,
193 PublicKey = keyPair.PublicBytes,
194 PrivateKey = keyPair.SecretBytes,
195 IsCompressed = keyPair.IsCompressed,
197 nTime = Interop.GetTime()
210 /// Check existance of item with provided KeyID
212 /// <param name="scriptID">Hash160 of public key.</param>
213 /// <returns>Checking result</returns>
214 public bool HaveKey(CKeyID keyID)
216 var QueryCount = dbConn.Query<NumQuery>("select count([ItemID]) from [KeyStorage] where [KeyID] = ?", keyID.hashBytes);
218 return QueryCount.First().Num == 1;
222 /// Get the key pair object.
224 /// <param name="keyID">Hash of public key.</param>
225 /// <param name="keyPair">Instance of CKeyPair or null.</param>
226 /// <returns>Result</returns>
227 public bool GetKey(CKeyID keyID, out CKeyPair keyPair)
229 var QueryGet = dbConn.Query<KeyStorageItem>("select * from [KeyStorage] where [KeyID] = ?", keyID.hashBytes);
231 if (QueryGet.Count() == 1)
233 keyPair = new CKeyPair(QueryGet.First().PrivateKey);
242 /// Add redeem script to script store.
244 /// <param name="script">CScript instance</param>
245 /// <returns>Result</returns>
246 public bool AddScript(CScript script)
250 var res = dbConn.Insert(new ScriptStorageItem()
252 ScriptID = script.ScriptID.hashBytes,
253 ScriptCode = script.Bytes
266 /// Check existance of item with provided ScriptID
268 /// <param name="scriptID">Hash160 of script code.</param>
269 /// <returns>Checking result</returns>
270 public bool HaveScript(CScriptID scriptID)
272 var QueryGet = dbConn.Query<NumQuery>("select count([ItemID]) from [ScriptStorage] where [ScriptID] = ?", scriptID.hashBytes);
274 return QueryGet.First().Num == 1;
278 /// Get redeem script from database.
280 /// <param name="scriptID">Script ID, evaluated as Hash160(script code).</param>
281 /// <param name="script">Instance of CScript</param>
282 /// <returns>Result</returns>
283 public bool GetScript(CScriptID scriptID, out CScript script)
285 var QueryGet = dbConn.Query<ScriptStorageItem>("select * from [ScriptStorage] where [ScriptID] = ?", scriptID.hashBytes);
287 if (QueryGet.Count() == 1)
289 script = new CScript(QueryGet.First().ScriptCode);
299 /// SQLite return type for ReserveKey
303 public int ItemId { get; set; }
304 public byte[] KeyID { get; set; }
308 /// Reserve key from a list of unused keys.
310 /// <param name="nKeyIndex">Internal index of key</param>
311 /// <returns>CKeyID instance</returns>
312 public CKeyID SelectKey(out int nKeyIndex)
314 var QueryGet = dbConn.Query<ReservedKey>("select ItemId, KeyID from [KeyStorage] where not IsUsed order by [nTime] asc limit 1");
316 if (QueryGet.Count() == 1)
318 var res = QueryGet.First();
320 nKeyIndex = res.ItemId;
322 return new CKeyID(res.KeyID);
326 // Generate new keys in case if keypool is exhausted.
328 GenerateKeys(nKeyPoolSize);
330 return SelectKey(out nKeyIndex);
335 /// Mark key as used.
337 /// <param name="nIndex">Internal index.</param>
338 public void MarkUsed(int nIndex)
342 dbConn.Execute("update [KeyStorage] set [IsUsed] = true where [ItemId] = ?", nIndex);
347 /// Mark key as unused.
349 /// <param name="nIndex">Internal index.</param>
350 public void MarkUnused(int nIndex)
354 dbConn.Execute("update [KeyStorage] set [IsUsed] = false where [ItemId] = ?", nIndex);
359 /// Regenerate all unused keys.
361 public void ResetPool()
365 dbConn.Execute("delete from [KeyStorage] where [IsUsed] = false");
366 GenerateKeys(nKeyPoolSize);
371 /// Timestamp of oldest item in keystore.
373 public int OldestTime
377 var QueryTime = dbConn.Query<NumQuery>("select [nTime] from [KeyStorage] where not [IsUsed] order by [ItemId] asc limit 1");
379 return QueryTime.First().Num;