//public CTemporaryBlock(CBlock Block, CPeer Sender) : base(Block.Header.BlockNumber, Block.Header.Hash, Block.Header.PreviousBlockHash, Block.Transiction, Block.Nonce, Block.Timestamp, Block.Difficulty) public CTemporaryBlock(CBlock Block, CPeer Sender) : base(Block.Header.BlockNumber, Block.Header.Hash, Block.Header.PreviousBlockHash, Block.TxLimit, Block.Nonce, Block.Timestamp, Block.Difficulty, Block.Transactions) { this.Nonce = Block.Nonce; this.Timestamp = Block.Timestamp; mSender = Sender; { } }
/// <summary> /// Aggiunge il proof of work(l'hash) al blocco. /// </summary> /// <param name="Block">Blocco su cui calcolare il proof of work.</param> public static void AddProof(CBlock Block) //TODO: implementare evento per l'uscita in caso sia stato trovato un blocco parallelo. Implementare multithreading { string hash = ""; bool found = false; while (!found) { Block.Timestamp = DateTime.Now; Block.Nonce++; //incremento della nonce per cambiare hash hash = HashBlock(Block); //calcola l'hash secondo il template di scrypt usato da litecoin found = true; for (int i = 0; i < Block.Difficulty && found; i++) { if (hash[i] != '0') { found = false; } } } if (Program.DEBUG) { CIO.DebugOut("Found hash for block " + Block.Header.BlockNumber + ": " + hash); } Block.Header.Hash = hash; }
/// <summary> /// Aggiunge i blocchi presenti nel vettore e ritorna l'indice dell'ultimo blocco aggiunto. /// </summary> /// <param name="Blocks"></param> /// <returns></returns> public ulong Add(CTemporaryBlock[] Blocks) { ulong counter = 0; string filepath = PATH + "\\" + FILENAME; //(!) e se scarico tutta la blockchain e da un certo punto in poi sbagliata? foreach (CTemporaryBlock b in Blocks) { if (b == null) { break; } if (CValidator.ValidateBlock(b, true)) { if (b.Header.BlockNumber == LastValidBlock.Header.BlockNumber) { break; } mLastValidBlock = b as CBlock; counter++; lock (Instance) { MemPool.Instance.RemoveBlock(b); UTXOManager.Instance.ApplyBlock(b); File.AppendAllText(filepath, (b as CBlock).Serialize() + '\n'); } } else { break; } } return(counter); }
/// <summary> /// Carica l'ultimo blocco della blockchain. /// </summary> private void Load() { string filepath = PATH + "\\" + FILENAME; string block = ""; mLastValidBlock = new CGenesisBlock(); if (File.Exists(filepath)) { StreamReader streamReader = new StreamReader(filepath); while ((block = streamReader.ReadLine()) != null) { CBlock b = JsonConvert.DeserializeObject <CBlock>(block); if (b.Header.BlockNumber > mLastValidBlock.Header.BlockNumber) { mLastValidBlock = b; } } streamReader.Close(); } else { File.WriteAllText(filepath, new CGenesisBlock().Serialize() + '\n'); mLastValidBlock = new CGenesisBlock(); } }
public CBlock RetriveBlock(ulong index, bool searchInSidechain = false) { string filepath = PATH + "\\" + FILENAME; string blockJson = ""; lock (Instance) { StreamReader streamReader = new StreamReader(filepath); while ((blockJson = streamReader.ReadLine()) != null) { CBlock block = JsonConvert.DeserializeObject <CBlock>(blockJson); if (block.Header.BlockNumber == index) { streamReader.Close(); return(block); } } streamReader.Close(); } if (searchInSidechain) { lock (mSideChain) if (LastBlock.Header.BlockNumber >= index) { return(mSideChain.RetriveBlock(index)); } } return(null); }
private CHeaderChain[] FindParallelChains(CBlock startBlock) { CHeader tmp1, tmp2; Stack <CHeaderChain> res = new Stack <CHeaderChain>(); int ID = 0; for (int i = 0; i < Peers.Length; i++) { if (Peers[i] != null) { ID = Peers[i].SendRequest(new CMessage(EMessageType.Request, ERequestType.GetLastHeader)); tmp1 = Peers[i].ReceiveHeader(ID, 5000); res.Push(new CHeaderChain()); res.Peek().AddPeer(Peers[i]); res.Peek().FinalIndex = tmp1.BlockNumber; Peers[i] = null; for (int j = i + 1; j < Peers.Length; j++) { ID = Peers[j].SendRequest(new CMessage(EMessageType.Request, ERequestType.GetLastHeader)); tmp2 = Peers[j].ReceiveHeader(ID, 5000); if (tmp1.Hash == tmp2.Hash) { res.Peek().AddPeer(Peers[j]); Peers[j] = null; } } } } return(res.ToArray()); }
private static CBlock GenerateNextBlock(int txLimit) { short newBlockDifficulty = Miner.CalculateDifficulty(); CBlock res = new CBlock(CBlockChain.Instance.LastBlock.Header.BlockNumber + 1, CBlockChain.Instance.LastBlock.Header.Hash, (ushort)newBlockDifficulty, 10); Console.WriteLine("Address: {0}", Utilities.Base64SHA2Hash(RSA.ExportPubKey(CServer.rsaKeyPair))); return(res); }
/// <summary> /// Esegue una richiesta ai peer collegati. /// </summary> /// <param name="rqs">Richiesta da effettuare.</param> /// <param name="arg">Parametro usato per passare un valore e/o ritornare un risultato quando necessario.</param> /// <returns></returns> public object DoRequest(ERequest rqs, object arg = null) //(!) rivedere i metodi di input/output del metodo { switch (rqs) { case ERequest.UpdatePeers: { UpdatePeers(); break; } case ERequest.LastValidBlock: { return(RequestLastValidBlock()); } case ERequest.DownloadMissingBlock: { object[] args = arg as object[]; ulong startingIndex = Convert.ToUInt64(args[0]); ulong finalIndex = Convert.ToUInt64(args[1]); return(DistribuiteDownloadBlocks(startingIndex, finalIndex)); } case ERequest.BroadcastMinedBlock: { CBlock b = arg as CBlock; foreach (CPeer p in Peers) { p.SendRequest(new CMessage(EMessageType.Request, ERequestType.NewBlockMined, EDataType.Block, b.Serialize())); } break; } case ERequest.LastCommonValidBlock: { return(FindLastCommonBlocks()); } case ERequest.FindParallelChain: { return(FindParallelChains(arg as CBlock)); } case ERequest.BroadcastNewTransaction: { foreach (CPeer p in Peers) { p.SendRequest(new CMessage(EMessageType.Request, ERequestType.NewTransaction, EDataType.Transaction, JsonConvert.SerializeObject(arg as Transaction))); //TODO : implementa richiesta di invio transazione } break; } default: throw new ArgumentException("Invalid request: " + rqs); } return(null); }
public static string HashBlock(CBlock block) { string toHash = block.Header.PreviousBlockHash + block.Nonce + block.Timestamp + block.MerkleRoot; //si concatenano vari parametri del blocco TODO: usare i parmetri giusti, quelli usati qua sono solo per dimostrazione e placeholder return(Utilities.ByteArrayToHexString( SCrypt.ComputeDerivedKey( Encoding.ASCII.GetBytes(toHash), Encoding.ASCII.GetBytes(toHash), 1024, 1, 1, 1, 32) )); //calcola l'hash secondo il template di scrypt usato da litecoin }
/// <summary> /// Inserisce le transazioni di un dato blocco nella mempool /// </summary> /// <param name="block">Il blocco da inserire</param> public void DumpBlock(CBlock block) { foreach (Transaction tx in block.Transactions) { if (!(tx.inputs.Count == 0)) { this.AddUTX(tx); } } }
/// <summary> /// Rimuove dalla mempool tutte le transazioni contenute in un blocco dato /// </summary> /// <param name="block"></param> public void RemoveBlock(CBlock block) { foreach (Transaction tx in block.Transactions) { if (this.CheckDouble(tx)) { this.RemoveUTX(tx.Hash); } } }
/// <summary> /// Calcola l'hash di un blocco e lo confronta al proof of work fornito per verificarne la validità /// </summary> /// <param name="block">Il blocco da confermare</param> /// <returns></returns> public static bool Verify(CBlock block) { if (block.Header.PreviousBlockHash == CBlockChain.Instance.RetriveBlock(block.Header.BlockNumber - 1).Header.Hash) { string toHash = block.Header.PreviousBlockHash + block.Nonce + block.Timestamp + block.MerkleRoot; if (block.Header.Hash == Utilities.ByteArrayToHexString(SCrypt.ComputeDerivedKey(Encoding.ASCII.GetBytes(toHash), Encoding.ASCII.GetBytes(toHash), 1024, 1, 1, 1, 32))) { return(true); } } return(false); }
public CBlock[] RetriveBlocks(ulong initialIndex, ulong finalIndex) { CBlock[] ris = new CBlock[finalIndex - initialIndex]; int c = 0; while (initialIndex < finalIndex) { ris[c++] = RetriveBlock(initialIndex); initialIndex++; } return(ris); }
public ulong AverageBlockTime(CBlock start, CBlock final) { TimeSpan timespan = final.Timestamp.Subtract(start.Timestamp); ulong seconds = (ulong)timespan.TotalSeconds; ulong nblock = final.Header.BlockNumber - start.Header.BlockNumber; try { return(seconds / nblock); } catch { return(60); } }
///<summary> ///Dato un blocco, rimuove tutti gli UTXO dal disco in base agli input contenuti, poi crea nuovi UTXO in base agli output ///</summary> public void ApplyBlock(CBlock block) { foreach (Transaction tx in block.Transactions) { if (tx.inputs != null) { foreach (Input input in tx.inputs) { if (input.OutputIndex != -1) { this.RemoveUTXO(Utilities.Base64SHA2Hash(tx.PubKey), input.TxHash, input.OutputIndex); } } } this.AddUTXO(new UTXO(tx.Hash, tx.outputs)); } }
public static bool ValidateBlock(CBlock block, bool CheckPreviusHash = false) { int coinbaseTx = 0; //TODO IMPORTANTE: aggiungere verifica timestamp if (block.Header.Hash != Miner.HashBlock(block)) { return(false); } if (CheckPreviusHash) { if (CBlockChain.Instance.RetriveBlock(block.Header.BlockNumber - 1)?.Header.Hash != block.Header.PreviousBlockHash) { return(false); } } /* * if (block.Difficulty < Miner.CalculateDifficulty() - 1 || block.Difficulty > Miner.CalculateDifficulty() + 1) * { * return false; * * } */ if (block.MerkleRoot != block.GenerateMerkleRoot()) { return(false); } foreach (Transaction tx in block.Transactions) { if (!tx.Verify()) { if (coinbaseTx == 0 && tx.inputs.Count == 1 && tx.inputs.First().TxHash == "0" && tx.inputs.First().OutputIndex == -1) { coinbaseTx++; } else { return(false); } } } return(true); }
private static bool ValidateRequest(CMessage Msg) { switch (Msg.RqsType) { case ERequestType.UpdPeers: { if (Msg.DataType == EDataType.NULL && Msg.Data == null) { return(true); } else { return(false); } } case ERequestType.NewBlockMined: { if (Msg.DataType == EDataType.Block && Msg.Data != null) { try { CBlock.Deserialize(Msg.Data); return(true); } catch { return(false); } } else { return(false); } } case ERequestType.ChainLength: { if (Msg.DataType == EDataType.NULL && Msg.Data == null) { return(true); } else { return(false); } } case ERequestType.GetHeader: { if (Msg.DataType == EDataType.ULong) { try { Convert.ToInt64(Msg.Data); } catch { return(false); } return(true); } else { return(false); } } case ERequestType.GetLastHeader: { { if (Msg.DataType == EDataType.NULL && Msg.Data == null) { return(true); } else { return(false); } } } case ERequestType.GetLastValid: { { if (Msg.DataType == EDataType.NULL && Msg.Data == null) { return(true); } else { return(false); } } } case ERequestType.DownloadBlock: { if (Msg.DataType == EDataType.ULong && Msg.Data != null) { try { Convert.ToUInt64(Msg.Data); return(true); } catch { return(false); } } return(false); } case ERequestType.DownloadBlocks: { if (Msg.DataType == EDataType.ULongList && Msg.Data != null) { try { string[] stringToConvert = Msg.Data.Split(';'); if (stringToConvert.Length != 2) { return(false); } foreach (string s in stringToConvert) { Convert.ToUInt64(s); } return(true); } catch { return(false); } } return(false); } case ERequestType.DownloadHeaders: { if (Msg.DataType == EDataType.ULongList && Msg.Data != null) { try { string[] stringToConvert = Msg.Data.Split(';'); if (stringToConvert.Length != 2) { return(false); } foreach (string s in stringToConvert) { Convert.ToUInt64(s); } return(true); } catch { return(false); } } return(false); } case ERequestType.NewTransaction: { if (Msg.DataType == EDataType.Transaction && Msg.Data != null) { try { Transaction t = JsonConvert.DeserializeObject <Transaction>(Msg.Data); if (MemPool.Instance.CheckDouble(t)) { return(false); } if (MemPool.Instance.CheckDoubleSpending(t)) { return(false); } if (CBlockChain.Instance.SideChain.CheckDouble(t)) { return(false); } MemPool.Instance.AddUTX(t); return(true); } catch { return(false); } } return(false); } default: return(false); } }
private void ExecuteRequest() { CMessage rqs; while (mIsConnected) { Thread.Sleep(100); if (RequestQueue.Count > 0) { rqs = RequestQueue.Dequeue(); switch (rqs.RqsType) { case ERequestType.UpdPeers: { if (Program.DEBUG) { CIO.DebugOut("UpdPeers received by " + mIp); } //(!) è meglio farsi ritornare la lista e poi usare json? SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.PeersList, CPeers.Instance.PeersList(), rqs.ID)); break; } case ERequestType.NewBlockMined: { if (CPeers.Instance.CanReceiveBlock) { if (Program.DEBUG) { CIO.DebugOut("NewBlockMined received by " + mIp); } CTemporaryBlock newBlock = new CTemporaryBlock(CBlock.Deserialize(rqs.Data), this); if (!CValidator.ValidateBlock(newBlock) && newBlock.Header.BlockNumber < CBlockChain.Instance.LastValidBlock.Header.BlockNumber) { Disconnect(); break; } //TODO scaricare i blocchi mancanti se ne mancano(sono al blocco 10 e mi arriva il blocco 50) if (!CBlockChain.Instance.AddNewMinedBlock(newBlock)) { Stack <CTemporaryBlock> blocks = new Stack <CTemporaryBlock>(); int ID = 0; blocks.Push(newBlock); for (ulong i = newBlock.Header.BlockNumber - 1; i > CBlockChain.Instance.LastValidBlock.Header.BlockNumber; i--) { ID = SendRequest(new CMessage(EMessageType.Request, ERequestType.DownloadBlock, EDataType.ULong, Convert.ToString(i))); blocks.Push(new CTemporaryBlock(JsonConvert.DeserializeObject <CBlock>(ReceiveData(ID, 5000).Data), this)); if (!CValidator.ValidateBlock(blocks.Peek()) && blocks.Peek().Header.BlockNumber < CBlockChain.Instance.LastValidBlock.Header.BlockNumber) { Disconnect(); break; } if (CBlockChain.Instance.AddNewMinedBlock(blocks.Peek())) { blocks.Pop(); for (int j = blocks.Count; j > 0; j--) { CBlockChain.Instance.AddNewMinedBlock(blocks.Pop()); } } } } } break; } case ERequestType.GetLastHeader: { if (Program.DEBUG) { CIO.DebugOut("GetLastHeader received by " + mIp); } SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.Header, JsonConvert.SerializeObject(CBlockChain.Instance.LastValidBlock.Header), rqs.ID)); break; } case ERequestType.ChainLength: { if (Program.DEBUG) { CIO.DebugOut("ChainLength received by " + mIp); } SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.ULong, Convert.ToString(CBlockChain.Instance.LastValidBlock.Header.BlockNumber), rqs.ID)); break; } case ERequestType.GetLastValid: { if (Program.DEBUG) { CIO.DebugOut("GetLastValid received by " + mIp); } SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.Block, CBlockChain.Instance.LastValidBlock.Serialize(), rqs.ID)); break; } case ERequestType.DownloadBlock: { if (Program.DEBUG) { CIO.DebugOut("DownloadBlock received by " + mIp); } SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.Block, JsonConvert.SerializeObject(CBlockChain.Instance.RetriveBlock(Convert.ToUInt64(rqs.Data), true)), rqs.ID)); break; } case ERequestType.DownloadBlocks: { if (Program.DEBUG) { CIO.DebugOut("DownloadBlocks received by " + mIp); } SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.BlockList, JsonConvert.SerializeObject(CBlockChain.Instance.RetriveBlocks(Convert.ToUInt64(rqs.Data.Split(';')[0]), Convert.ToUInt64(rqs.Data.Split(';')[1]))), rqs.ID)); break; } case ERequestType.DownloadHeaders: { if (Program.DEBUG) { CIO.DebugOut("DownloadBlocks received by " + mIp); } SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.HeaderList, JsonConvert.SerializeObject(CBlockChain.Instance.RetriveHeaders(Convert.ToUInt64(rqs.Data.Split(';')[0]), Convert.ToUInt64(rqs.Data.Split(';')[1]))), rqs.ID)); break; } case ERequestType.GetHeader: { if (Program.DEBUG) { CIO.DebugOut("GetLastHeader received by " + mIp); } SendRequest(new CMessage(EMessageType.Data, ERequestType.NULL, EDataType.Header, JsonConvert.SerializeObject(CBlockChain.Instance.RetriveBlock(Convert.ToUInt64(rqs.Data)).Header), rqs.ID)); break; } case ERequestType.NewTransaction: { Transaction t = JsonConvert.DeserializeObject <Transaction>(rqs.Data); if (t.Verify()) { MemPool.Instance.AddUTX(t); } break; } default: if (Program.DEBUG) { CIO.DebugOut("Ricevuto comando sconosciuto: " + rqs.RqsType + " da " + IP); } break; } } } }
private static bool ValidateData(CMessage Msg) { if (Msg.RqsType != ERequestType.NULL) { return(false); } switch (Msg.DataType) { case EDataType.PeersList: try { string[] peers = Msg.Data.Split(';'); foreach (string rp in peers) { CPeer.Deserialize(rp); } return(true); } catch { return(false); } case EDataType.Block: try { CBlock.Deserialize(Msg.Data); return(true); } catch { return(false); } case EDataType.ULong: try { Convert.ToUInt64(Msg.Data); return(true); } catch { return(false); } case EDataType.ULongList: try { JsonConvert.DeserializeObject <ulong[]>(Msg.Data); return(true); } catch { return(false); } case EDataType.Header: try { JsonConvert.DeserializeObject <CHeader>(Msg.Data); return(true); } catch { return(false); } case EDataType.BlockList: try { JsonConvert.DeserializeObject <CBlock[]>(Msg.Data); return(true); } catch { return(false); } case EDataType.HeaderList: try { JsonConvert.DeserializeObject <CHeader[]>(Msg.Data); return(true); } catch { return(false); } default: return(false); } }
public static short CalculateDifficulty() { int numberOfBlocks = 300; CBlock lastBlock = CBlockChain.Instance.LastBlock; CBlock previousBlock; short newBlockDifficulty = 0; ulong highAverangeTimeLimit = 70, lowAverangeTimeLimit = 30; ulong averangeBlockTime = 0; if (lastBlock.Header.BlockNumber > (ulong)numberOfBlocks) { previousBlock = CBlockChain.Instance.RetriveBlock(lastBlock.Header.BlockNumber - (ulong)numberOfBlocks, true); } else { previousBlock = CBlockChain.Instance.RetriveBlock(1, true); } if (previousBlock != null) { averangeBlockTime = CBlockChain.Instance.AverageBlockTime(previousBlock, lastBlock); //in secondi } else { averangeBlockTime = 60; } if (averangeBlockTime > highAverangeTimeLimit) { newBlockDifficulty = (short)(lastBlock.Difficulty - 1); if (newBlockDifficulty <= 0) { newBlockDifficulty = 1; } if (Program.DEBUG) { CIO.DebugOut("La nuova difficoltà è: " + newBlockDifficulty); //Thread.Sleep(1000); } } else if (averangeBlockTime < lowAverangeTimeLimit) { newBlockDifficulty = (short)(lastBlock.Difficulty + 1); if (newBlockDifficulty >= 4) { newBlockDifficulty = 4; } if (Program.DEBUG) { CIO.DebugOut("La nuova difficoltà è: " + newBlockDifficulty); //Thread.Sleep(1000); } } else { newBlockDifficulty = (short)lastBlock.Difficulty; if (Program.DEBUG) { CIO.DebugOut("La nuova difficoltà è: " + newBlockDifficulty); //Thread.Sleep(1000); } } return(3); }
private CBlock FindLastCommonBlocks() { ulong minLength = ulong.MaxValue, tmp = 0; int rqsID = 0; bool found = false; Stack <CHeader> headers = new Stack <CHeader>(); CBlock res = null; foreach (CPeer p in Peers) { try { rqsID = p.SendRequest(new CMessage(EMessageType.Request, ERequestType.ChainLength)); tmp = p.ReceiveULong(rqsID, 5000); if (tmp < minLength) { minLength = tmp; } } catch (SocketException) { } p.Socket.ReceiveTimeout = 0; } CRange r = new CRange(CBlockChain.Instance.LastValidBlock.Header.BlockNumber, minLength); if (r.End != ulong.MaxValue && r.Start < r.End) { //trova l'ultimo blocco uguale tra i peer e salva l'indice di quel blocco in r.start int ID = 0; while (r.Start < r.End) { found = true; tmp = (r.Start + r.End) / 2; if (r.End - r.Start == 1) { tmp++; } foreach (CPeer p in Peers) { ID = p.SendRequest(new CMessage(EMessageType.Request, ERequestType.GetHeader, EDataType.ULong, Convert.ToString(tmp))); headers.Push(p.ReceiveHeader(ID, 5000)); } while (headers.Count > 1 && found) { if (!(headers?.Pop().Hash == headers?.Peek().Hash)) { found = false; } } if (headers.Count == 1) { headers.Pop(); } //se tutti i blocchi sono uguali allora found=true, mentre se ce n'è qualcuno di diverso found=false if (found) { r.Start = tmp; } else if (!(r.End - r.Start == 1)) { r.End = tmp; } else { r.End--; } } //controlla se l'ultimo blocco è valido? foreach (CPeer p in Peers) { ID = p.SendRequest(new CMessage(EMessageType.Request, ERequestType.DownloadBlock, EDataType.ULong, Convert.ToString(r.Start))); res = p.ReceiveBlock(ID, 5000); if (res != null && CValidator.ValidateBlock(res)) { break; } else { p.Disconnect(); } p.Socket.ReceiveTimeout = 0; } return(res); } else { return(CBlockChain.Instance.LastValidBlock); } }