public static async Task <Record> GetRecordVersion(this ILedgerQueries queries, ByteString key, ByteString version) { if (version.Value.Count == 0) { return(new Record(key, ByteString.Empty, ByteString.Empty)); } else { ByteString rawTransaction = await queries.GetTransaction(version); if (rawTransaction == null) { return(null); } else { Transaction transaction = MessageSerializer.DeserializeTransaction(rawTransaction); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); Record result = mutation.Records.FirstOrDefault(record => record.Key.Equals(key) && record.Value != null); if (result == null) { return(null); } else { return(result); } } } }
public async Task AddTransactions(IEnumerable <ByteString> transactions) { using (SqliteTransaction context = Connection.BeginTransaction(IsolationLevel.Serializable)) { foreach (ByteString rawTransaction in transactions) { byte[] rawTransactionBuffer = rawTransaction.ToByteArray(); Transaction transaction = MessageSerializer.DeserializeTransaction(rawTransaction); byte[] transactionHash = MessageSerializer.ComputeHash(rawTransactionBuffer); byte[] mutationHash = MessageSerializer.ComputeHash(transaction.Mutation.ToByteArray()); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); await UpdateAccounts(mutation, mutationHash); IReadOnlyList <long> rowId = await ExecuteAsync(@" INSERT INTO Transactions (Hash, MutationHash, RawData) VALUES (@hash, @mutationHash, @rawData); SELECT last_insert_rowid();", reader => (long)reader.GetValue(0), new Dictionary <string, object>() { ["@hash"] = transactionHash, ["@mutationHash"] = mutationHash, ["@rawData"] = rawTransactionBuffer }); await AddTransaction(rowId[0], mutationHash, mutation); } context.Commit(); } }
public async Task AddTransactions(IEnumerable <ByteString> transactions) { using (SqliteTransaction context = Connection.BeginTransaction(System.Data.IsolationLevel.Serializable)) { foreach (ByteString rawTransaction in transactions) { byte[] rawTransactionBuffer = rawTransaction.ToByteArray(); Transaction transaction = MessageSerializer.DeserializeTransaction(rawTransaction); byte[] transactionHash = MessageSerializer.ComputeHash(rawTransactionBuffer); byte[] mutationHash = MessageSerializer.ComputeHash(transaction.Mutation.ToByteArray()); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); await UpdateAccounts(mutation, mutationHash); await ExecuteAsync(@" INSERT INTO Transactions (Hash, MutationHash, RawData) VALUES (@hash, @mutationHash, @rawData)", new Dictionary <string, object>() { ["@hash"] = transactionHash, ["@mutationHash"] = mutationHash, ["@rawData"] = rawTransactionBuffer }); await AddTransaction(mutation); } context.Commit(); } }
public async Task AddTransactions(IEnumerable <ByteString> transactions) { using (SqlTransaction context = Connection.BeginTransaction(IsolationLevel.Snapshot)) { foreach (ByteString rawTransaction in transactions) { byte[] rawTransactionBuffer = rawTransaction.ToByteArray(); Transaction transaction = MessageSerializer.DeserializeTransaction(rawTransaction); byte[] transactionHash = MessageSerializer.ComputeHash(rawTransactionBuffer); byte[] mutationHash = MessageSerializer.ComputeHash(transaction.Mutation.ToByteArray()); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); IReadOnlyList <Record> conflicts = await ExecuteQuery <Record>( "EXEC [Openchain].[AddTransaction] @instance, @transactionHash, @mutationHash, @rawData, @records;", reader => mutation.Records.First(record => record.Key.Equals(new ByteString((byte[])reader[0]))), new Dictionary <string, object>() { ["instance"] = this.instanceId, ["transactionHash"] = transactionHash, ["mutationHash"] = mutationHash, ["rawData"] = rawTransactionBuffer, ["type:records"] = "Openchain.RecordMutationTable", ["records"] = mutation.Records.Select(record => { SqlDataRecord result = new SqlDataRecord(recordMutationMetadata); RecordKey key = ParseRecordKey(record.Key); result.SetBytes(0, 0, record.Key.ToByteArray(), 0, record.Key.Value.Count); if (record.Value == null) { result.SetDBNull(1); } else { result.SetBytes(1, 0, record.Value.ToByteArray(), 0, record.Value.Value.Count); } result.SetBytes(2, 0, record.Version.ToByteArray(), 0, record.Version.Value.Count); result.SetString(3, key.Name); result.SetByte(4, (byte)key.RecordType); return(result); }).ToList() }, context); if (conflicts.Count > 0) { throw new ConcurrentMutationException(conflicts[0]); } } context.Commit(); } }
public async Task GetTransaction_Success() { await AddRecords("/:DATA:name1"); ByteString mutation = await AddRecords("/:DATA:name2"); await AddRecords("/:DATA:name3"); ByteString result = await Queries.GetTransaction(mutation); Assert.Equal(mutation, new ByteString(MessageSerializer.ComputeHash(MessageSerializer.DeserializeTransaction(result).Mutation.ToByteArray()))); }
public void Transaction_Success() { Transaction transaction = new Transaction( binaryData[0], new DateTime(1, 2, 3, 4, 5, 6), binaryData[1]); byte[] result = MessageSerializer.SerializeTransaction(transaction); Transaction finalTransaction = MessageSerializer.DeserializeTransaction(new ByteString(result)); Assert.Equal(79, result.Length); Assert.Equal(transaction.Mutation, finalTransaction.Mutation); Assert.Equal(transaction.Timestamp, finalTransaction.Timestamp); Assert.Equal(transaction.TransactionMetadata, finalTransaction.TransactionMetadata); }
private JsonResult TransactionToJson(ByteString rawData) { Transaction transaction = MessageSerializer.DeserializeTransaction(rawData); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); return(Json(new { transaction_hash = new ByteString(MessageSerializer.ComputeHash(rawData.ToByteArray())).ToString(), mutation_hash = new ByteString(MessageSerializer.ComputeHash(transaction.Mutation.ToByteArray())).ToString(), mutation = new { @namespace = mutation.Namespace.ToString(), records = mutation.Records.Select(GetRecordJson).ToArray(), metadata = mutation.Metadata.ToString() }, timestamp = transaction.Timestamp, transaction_metadata = transaction.TransactionMetadata.ToString() })); }
public async Task <IList <OutboundTransaction> > GetUnprocessedTransactions() { string account = $"/asset/{assetName}/out/"; string asset = $"/asset/{assetName}/"; HttpClient client = new HttpClient(); HttpResponseMessage accountsResponse = await client.GetAsync(new Uri(tedChainUri, $"query/subaccounts?account={account}")); JArray records = JArray.Parse(await accountsResponse.EnsureSuccessStatusCode().Content.ReadAsStringAsync()); List <OutboundTransaction> result = new List <OutboundTransaction>(); foreach (JObject record in records) { ByteString mutationHash = ByteString.Parse((string)record["version"]); ByteString key = ByteString.Parse((string)record["key"]); RecordKey recordKey = RecordKey.Parse(key); if (recordKey.RecordType != RecordType.Account || recordKey.Name != $"/asset/{assetName}/") { continue; } HttpResponseMessage transactionResponse = await client.GetAsync(new Uri(tedChainUri, $"query/transaction?mutation_hash={mutationHash}")); JObject rawTransaction = JObject.Parse(await transactionResponse.EnsureSuccessStatusCode().Content.ReadAsStringAsync()); Transaction transaction = MessageSerializer.DeserializeTransaction(ByteString.Parse((string)rawTransaction["raw"])); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); // TODO: Validate that the record mutation has an empty version string target = GetPayingAddress(mutation); if (target != null) { long value = ParseInt(ByteString.Parse((string)record["value"])); result.Add(new OutboundTransaction(key, value, mutationHash, target)); } } return(result); }
public async Task AddTransactions(IEnumerable <ByteString> transactions) { using (var dbTransaction = Context.Database.BeginTransaction(IsolationLevel.Serializable)) { try { foreach (ByteString rawTransaction in transactions) { byte[] rawTransactionBuffer = rawTransaction.ToByteArray(); Transaction transaction = MessageSerializer.DeserializeTransaction(rawTransaction); byte[] transactionHash = MessageSerializer.ComputeHash(rawTransactionBuffer); byte[] mutationHash = MessageSerializer.ComputeHash(transaction.Mutation.ToByteArray()); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); await UpdateAccounts(mutation, mutationHash); var newTransaction = new Models.Transaction { TransactionHash = transactionHash, MutationHash = mutationHash, RawData = rawTransactionBuffer }; Transactions.Add(newTransaction); await Context.SaveChangesAsync(); await AddTransaction(newTransaction.Id, mutationHash, mutation); } dbTransaction.Commit(); } catch (Exception ex) { _logger.LogError(ex.Message); dbTransaction.Rollback(); throw new Exception(ex.Message); } } }
public async Task <Mutation> GetMutation(ByteString hash) { Mutation mutation; if (!Transactions.TryGetValue(hash, out mutation)) { string account = $"/asset/{assetName}/out/"; string asset = $"/asset/{assetName}/"; HttpClient client = new HttpClient(); HttpResponseMessage getTransactionResponse = await client.GetAsync(new Uri(tedChainUri, $"query/transaction?mutation_hash={hash.ToString()}")); JToken rawTransaction = JToken.Parse(await getTransactionResponse.EnsureSuccessStatusCode().Content.ReadAsStringAsync()); Transaction transaction = MessageSerializer.DeserializeTransaction(ByteString.Parse((string)rawTransaction["raw"])); mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); Transactions.Add(hash, mutation); } return(mutation); }
private static ByteString GetMutationHash(ByteString transaction) { return(new ByteString( MessageSerializer.ComputeHash(MessageSerializer.DeserializeTransaction(transaction).Mutation.ToByteArray()))); }
public async Task AddTransactions(IEnumerable <ByteString> transactions) { List <byte[]> transactionHashes = new List <byte[]>(); List <Record> lockedRecords = new List <Record>(); byte[] lockToken = Guid.NewGuid().ToByteArray(); try { foreach (ByteString rawTransaction in transactions) { byte[] rawTransactionBuffer = rawTransaction.ToByteArray(); Transaction transaction = MessageSerializer.DeserializeTransaction(rawTransaction); byte[] transactionHash = MessageSerializer.ComputeHash(rawTransactionBuffer); byte[] mutationHash = MessageSerializer.ComputeHash(transaction.Mutation.ToByteArray()); Mutation mutation = MessageSerializer.DeserializeMutation(transaction.Mutation); List <byte[]> records = new List <byte[]>(); #if DEBUG Logger.LogDebug($"Add transaction {new ByteString(transactionHash)} token {new ByteString(lockToken)}"); #endif transactionHashes.Add(transactionHash); // add pending transaction var ptr = new MongoDbPendingTransaction { MutationHash = mutationHash, TransactionHash = transactionHash, RawData = rawTransactionBuffer, LockTimestamp = DateTime.UtcNow, InitialRecords = new List <MongoDbRecord>(), AddedRecords = new List <byte[]>(), LockToken = lockToken }; await PendingTransactionCollection.InsertOneAsync(ptr); // lock records foreach (var r in mutation.Records) { var previous = await LockRecord(lockToken, r); if (previous != null) { ptr.InitialRecords.Add(previous); lockedRecords.Add(r); } else if (r.Value != null) { ptr.AddedRecords.Add(r.Key.ToByteArray()); lockedRecords.Add(r); } } // save original records await PendingTransactionCollection.UpdateOneAsync( x => x.TransactionHash.Equals(transactionHash), Builders <MongoDbPendingTransaction> .Update .Set(x => x.InitialRecords, ptr.InitialRecords) .Set(x => x.AddedRecords, ptr.AddedRecords) ); // update records foreach (var rec in mutation.Records) { MongoDbRecord r = BuildMongoDbRecord(rec); if (r.Value == null) { if (r.Version.Length == 0) // No record expected { var res = await RecordCollection.CountAsync(x => x.Key.Equals(r.Key)); if (res != 0) // a record exists { throw new ConcurrentMutationException(rec); } } else { // specific version expected var res = await RecordCollection.CountAsync(x => x.Key.Equals(r.Key) && x.Version.Equals(r.Version)); if (res != 1) // expected version not found { throw new ConcurrentMutationException(rec); } } } else { if (r.Version.Length == 0) { r.Version = mutationHash; r.TransactionLock = lockToken; try { await RecordCollection.InsertOneAsync(r); } catch (MongoWriteException ex) when(ex.WriteError.Category == ServerErrorCategory.DuplicateKey) { throw new ConcurrentMutationException(rec); } } else { var res = await RecordCollection.UpdateOneAsync( x => x.Key.Equals(r.Key) && x.Version.Equals(r.Version) && x.TransactionLock.Equals(lockToken), Builders <MongoDbRecord> .Update .Set(x => x.Value, r.Value) .Set(x => x.Version, mutationHash) ); if (res.MatchedCount != 1 || res.ModifiedCount != 1) { throw new ConcurrentMutationException(rec); } } records.Add(r.Key); } } // add transaction var tr = new MongoDbTransaction { MutationHash = mutationHash, TransactionHash = transactionHash, RawData = rawTransactionBuffer, Records = records }; await TransactionCollection.InsertOneAsync(tr); } // unlock records List <ByteString> l = new List <ByteString>(); foreach (var r in lockedRecords) { if (!l.Contains(r.Key)) { await UnlockRecord(lockToken, r); l.Add(r.Key); } } // remove pending transaction foreach (var hash in transactionHashes) { await PendingTransactionCollection.DeleteOneAsync(x => x.TransactionHash.Equals(hash)); } #if DEBUG Logger.LogDebug($"Transaction committed token {new ByteString(lockToken)}"); #endif } catch (Exception ex1) { #if DEBUG Logger.LogDebug($"Error committing transaction batch {ex1.Message} token {new ByteString(lockToken)}"); #endif foreach (var hash in transactionHashes) { #if DEBUG Logger.LogDebug($"Rollbacking transaction 0 {new ByteString(hash)}"); #endif try { await RollbackTransaction(hash); } catch (Exception ex2) { throw new AggregateException(ex2, ex1); } } throw; } }
public void Transaction_Invalid() { Assert.Throws <InvalidProtocolBufferException>(() => MessageSerializer.DeserializeTransaction(ByteString.Parse("01"))); }