public async Task Run(CancellationToken cancel) { while (!cancel.IsCancellationRequested) { try { if (await this.anchorRecorder.CanRecordAnchor()) { LedgerAnchor anchor = await this.anchorBuilder.CreateAnchor(); if (anchor != null) { logger.LogInformation($"Recording anchor for {anchor.TransactionCount} transaction(s)"); // Record the anchor await this.anchorRecorder.RecordAnchor(anchor); // Commit the anchor if it has been recorded successfully await this.anchorBuilder.CommitAnchor(anchor); } } } catch (Exception exception) { logger.LogError($"Error in the anchor worker:\r\n{exception}"); // Wait longer if an error occurred await Task.Delay(TimeSpan.FromMinutes(1), cancel); } await Task.Delay(TimeSpan.FromSeconds(10), cancel); } }
public async Task RecordAnchor_ZeroTransaction() { LedgerAnchor anchor = await this.anchorBuilder.RecordAnchor(); Assert.Null(anchor); Assert.Equal(0, recorder.Anchors.Count); Assert.Null(state.LastAnchor); }
public async Task <LedgerAnchor> GetLastAnchor() { var anchor = await Anchors.OrderBy(a => a.Id).Take(1).FirstOrDefaultAsync(); var ledgerAnchor = new LedgerAnchor(new ByteString(anchor.Position), new ByteString(anchor.FullLedgerHash), anchor.TransactionCount); return(ledgerAnchor); }
public async Task CreateAnchor_CannotRecord() { recorder.Enabled = false; LedgerAnchor anchor = await this.anchorBuilder.RecordAnchor(); Assert.Null(anchor); Assert.Equal(0, recorder.Anchors.Count); Assert.Null(state.LastAnchor); }
/// <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 AnchorStateCollection.InsertOneAsync(new MongoDbAnchorStateRecord { Position = anchor.Position.ToByteArray(), FullLedgerHash = anchor.FullStoreHash.ToByteArray(), TransactionCount = anchor.TransactionCount }); }
public async Task RecordAnchor_OneTransaction() { ByteString hash = AddRecord("key1"); ByteString expectedCumulativeHash = CombineHashes(new ByteString(new byte[32]), hash); LedgerAnchor anchor = await this.anchorBuilder.RecordAnchor(); AssertAnchor(anchor, 1, hash, expectedCumulativeHash); Assert.Equal(1, recorder.Anchors.Count); AssertAnchor(recorder.Anchors[0], 1, hash, expectedCumulativeHash); AssertAnchor(state.LastAnchor, 1, hash, expectedCumulativeHash); }
public async Task GetLastAnchor_Success() { await this.anchorBuilder.CommitAnchor(new LedgerAnchor(binaryData[0], binaryData[1], 100)); await this.anchorBuilder.CommitAnchor(new LedgerAnchor(binaryData[2], binaryData[3], 101)); LedgerAnchor anchor = await this.anchorBuilder.GetLastAnchor(); Assert.Equal(binaryData[2], anchor.Position); Assert.Equal(binaryData[3], anchor.FullStoreHash); Assert.Equal(101, anchor.TransactionCount); }
public async Task CommitAnchor(LedgerAnchor anchor) { var newAnchor = new Models.Anchor { Position = anchor.Position.ToByteArray(), FullLedgerHash = anchor.FullStoreHash.ToByteArray(), TransactionCount = anchor.TransactionCount }; Anchors.Add(newAnchor); await Context.SaveChangesAsync(); }
public async Task RecordAnchor_OnePlusOneTransaction() { state.LastAnchor = new LedgerAnchor(binaryData[1], binaryData[2], 1); ByteString hash = AddRecord("key2"); ByteString expectedCumulativeHash = CombineHashes(binaryData[2], hash); LedgerAnchor anchor = await this.anchorBuilder.RecordAnchor(); AssertAnchor(anchor, 2, hash, expectedCumulativeHash); Assert.Equal(1, recorder.Anchors.Count); AssertAnchor(recorder.Anchors[0], 2, hash, expectedCumulativeHash); AssertAnchor(state.LastAnchor, 2, hash, expectedCumulativeHash); }
/// <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 }); }
public async Task Run(CancellationToken cancel) { IServiceScopeFactory scopeFactory = services.GetService <IServiceScopeFactory>(); ILogger logger = services.GetRequiredService <ILogger>(); while (!cancel.IsCancellationRequested) { using (IServiceScope scope = scopeFactory.CreateScope()) { IAnchorRecorder anchorRecorder = scope.ServiceProvider.GetService <IAnchorRecorder>(); IAnchorState anchorState = scope.ServiceProvider.GetService <IAnchorState>(); if (anchorRecorder == null || anchorState == null) { logger.LogInformation("Anchoring disabled"); return; } IStorageEngine storageEngine = scope.ServiceProvider.GetRequiredService <IStorageEngine>(); try { await storageEngine.Initialize(); await anchorState.Initialize(); AnchorBuilder anchorBuilder = new AnchorBuilder(storageEngine, anchorRecorder, anchorState); while (!cancel.IsCancellationRequested) { LedgerAnchor anchor = await anchorBuilder.RecordAnchor(); if (anchor != null) { logger.LogInformation($"Recorded an anchor for {anchor.TransactionCount} transactions: {anchor.FullStoreHash.ToString()}"); } await Task.Delay(TimeSpan.FromSeconds(10), cancel); } } catch (Exception exception) { logger.LogError($"Error in the anchor worker:\r\n{exception}"); // Wait longer if an error occurred await Task.Delay(TimeSpan.FromMinutes(1), cancel); } } } }
/// <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); //Changed the builder constructor //TODO: make sure transa TransactionBuilder builder = Network.Main.CreateTransactionBuilder(); 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, 0L); builder.SendFees(satoshiFees); builder.SetChange(this.publishingAddress.ScriptPubKey, ChangeType.All); ByteString seriazliedTransaction = new ByteString(builder.BuildTransaction(true).ToBytes()); await SubmitTransaction(seriazliedTransaction); } }
/// <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); } }
public Task CommitAnchor(LedgerAnchor anchor) { this.LastAnchor = anchor; return Task.FromResult(0); }
public Task RecordAnchor(LedgerAnchor anchor) { Anchors.Add(anchor); return Task.FromResult(0); }
private void AssertAnchor(LedgerAnchor anchor, long transactionCount, ByteString position, ByteString fullStoreHash) { Assert.Equal(transactionCount, anchor.TransactionCount); Assert.Equal(position, anchor.Position); Assert.Equal(fullStoreHash, anchor.FullStoreHash); }
private void AssertAnchor(LedgerAnchor anchor, long transactionCount, ByteString position, ByteString fullStoreHash) { Assert.Equal(transactionCount, anchor.TransactionCount); Assert.Equal(position, anchor.Position); Assert.Equal(fullStoreHash, anchor.FullStoreHash); }
public async Task GetLastAnchor_NoAnchor() { LedgerAnchor anchor = await this.anchorBuilder.GetLastAnchor(); Assert.Null(anchor); }
public Task RecordAnchor(LedgerAnchor anchor) { Anchors.Add(anchor); return(Task.FromResult(0)); }
public Task CommitAnchor(LedgerAnchor anchor) { this.LastAnchor = anchor; return(Task.FromResult(0)); }
/// <summary> /// Creates a database anchor for the current state of the database. /// </summary> /// <returns>The task object representing the asynchronous operation.</returns> public async Task <LedgerAnchor> CreateAnchor() { IEnumerable <LedgerAnchor> anchors = await ExecuteAsync(@" SELECT Position, FullLedgerHash, TransactionCount FROM Anchors ORDER BY Id DESC LIMIT 1", reader => new LedgerAnchor( new ByteString((byte[])reader.GetValue(0)), new ByteString((byte[])reader.GetValue(1)), reader.GetInt64(2)), new Dictionary <string, object>()); LedgerAnchor lastAnchor = anchors.FirstOrDefault(); IReadOnlyList <ByteString> newTransactions; byte[] currentHash; if (lastAnchor != null) { newTransactions = await ExecuteAsync(@" SELECT Hash FROM Transactions WHERE Id > (SELECT Id FROM Transactions WHERE Hash = @hash) ORDER BY Id", reader => new ByteString((byte[])reader.GetValue(0)), new Dictionary <string, object>() { ["@hash"] = lastAnchor.Position.ToByteArray() }); currentHash = lastAnchor.FullStoreHash.ToByteArray(); } else { newTransactions = await ExecuteAsync(@" SELECT Hash FROM Transactions ORDER BY Id", reader => new ByteString((byte[])reader.GetValue(0)), new Dictionary <string, object>()); currentHash = new byte[32]; } if (newTransactions.Count == 0) { return(null); } byte[] buffer = new byte[64]; using (SHA256 sha = SHA256.Create()) { foreach (ByteString transactionHash in newTransactions) { currentHash.CopyTo(buffer, 0); transactionHash.CopyTo(buffer, 32); currentHash = sha.ComputeHash(sha.ComputeHash(buffer)); } } LedgerAnchor result = new LedgerAnchor( newTransactions[newTransactions.Count - 1], new ByteString(currentHash), newTransactions.Count + (lastAnchor != null ? lastAnchor.TransactionCount : 0)); return(result); }