/// <summary> /// Marks the anchor as successfully recorded in the anchoring medium. /// </summary> /// <param name="anchor">The anchor to commit.</param> /// <returns>The task object representing the asynchronous operation.</returns> public async Task CommitAnchor(LedgerAnchor anchor) { await ExecuteAsync(@" INSERT INTO Anchors (Position, FullLedgerHash, TransactionCount) VALUES (@position, @fullLedgerHash, @transactionCount)", new Dictionary<string, object>() { ["@position"] = anchor.Position.ToByteArray(), ["@fullLedgerHash"] = anchor.FullStoreHash.ToByteArray(), ["@transactionCount"] = anchor.TransactionCount }); }
private async Task <LedgerAnchor> ComputeNewAnchor(LedgerAnchor lastAnchor) { IReadOnlyList <ByteString> newTransactions; byte[] currentHash; if (lastAnchor != null) { newTransactions = await storageEngine.GetTransactions(lastAnchor.Position); currentHash = lastAnchor.FullStoreHash.ToByteArray(); } else { newTransactions = await storageEngine.GetTransactions(null); currentHash = new byte[32]; } if (newTransactions.Count == 0) { return(null); } byte[] position = currentHash; byte[] buffer = new byte[64]; using (SHA256 sha = SHA256.Create()) { foreach (ByteString rawTransaction in newTransactions) { currentHash.CopyTo(buffer, 0); position = MessageSerializer.ComputeHash(rawTransaction.ToByteArray()); position.CopyTo(buffer, 32); currentHash = sha.ComputeHash(sha.ComputeHash(buffer)); } } LedgerAnchor result = new LedgerAnchor( new ByteString(position), new ByteString(currentHash), newTransactions.Count + (lastAnchor != null ? lastAnchor.TransactionCount : 0)); return(result); }
/// <summary> /// Records a database anchor. /// </summary> /// <param name="anchor">The anchor to be recorded.</param> /// <returns>The task object representing the asynchronous operation.</returns> public async Task RecordAnchor(LedgerAnchor anchor) { byte[] anchorPayload = anchorMarker .Concat(BitConverter.GetBytes((ulong)anchor.TransactionCount).Reverse()) .Concat(anchor.FullStoreHash.ToByteArray()) .ToArray(); using (HttpClient client = new HttpClient()) { BitcoinAddress address = this.publishingAddress.ScriptPubKey.GetDestinationAddress(this.network); HttpResponseMessage response = await client.GetAsync(new Uri(url, $"addresses/{address.ToString()}/unspents")); response.EnsureSuccessStatusCode(); string body = await response.Content.ReadAsStringAsync(); JArray outputs = JArray.Parse(body); TransactionBuilder builder = new TransactionBuilder(); builder.AddKeys(publishingAddress.GetBitcoinSecret(network)); foreach (JObject output in outputs) { string transactionHash = (string)output["transaction_hash"]; uint outputIndex = (uint)output["output_index"]; long amount = (long)output["value"]; builder.AddCoins(new Coin(uint256.Parse(transactionHash), outputIndex, new Money(amount), publishingAddress.ScriptPubKey)); } Script opReturn = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(anchorPayload)); builder.Send(opReturn, 0); builder.SendFees(satoshiFees); builder.SetChange(this.publishingAddress.ScriptPubKey, ChangeType.All); ByteString seriazliedTransaction = new ByteString(builder.BuildTransaction(true).ToBytes()); await SubmitTransaction(seriazliedTransaction); } }
private async Task<LedgerAnchor> ComputeNewAnchor(LedgerAnchor lastAnchor) { IReadOnlyList<ByteString> newTransactions; byte[] currentHash; if (lastAnchor != null) { newTransactions = await storageEngine.GetTransactions(lastAnchor.Position); currentHash = lastAnchor.FullStoreHash.ToByteArray(); } else { newTransactions = await storageEngine.GetTransactions(null); currentHash = new byte[32]; } if (newTransactions.Count == 0) return null; byte[] position = currentHash; byte[] buffer = new byte[64]; using (SHA256 sha = SHA256.Create()) { foreach (ByteString rawTransaction in newTransactions) { currentHash.CopyTo(buffer, 0); position = MessageSerializer.ComputeHash(rawTransaction.ToByteArray()); position.CopyTo(buffer, 32); currentHash = sha.ComputeHash(sha.ComputeHash(buffer)); } } LedgerAnchor result = new LedgerAnchor( new ByteString(position), new ByteString(currentHash), newTransactions.Count + (lastAnchor != null ? lastAnchor.TransactionCount : 0)); return result; }
/// <summary> /// Determines whether a new anchor should be calculated and recorded, /// and does it if it is the case. /// </summary> /// <returns>The task object representing the asynchronous operation.</returns> public async Task <LedgerAnchor> RecordAnchor() { if (await anchorRecorder.CanRecordAnchor()) { LedgerAnchor anchor = await anchorState.GetLastAnchor(); LedgerAnchor latestAnchor = await ComputeNewAnchor(anchor); if (latestAnchor != null) { // Record the anchor await anchorRecorder.RecordAnchor(latestAnchor); // Commit the anchor if it has been recorded successfully await anchorState.CommitAnchor(latestAnchor); return(latestAnchor); } } return(null); }