예제 #1
0
        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();
            }
        }
예제 #3
0
        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();
            }
        }
예제 #5
0
        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())));
 }
예제 #12
0
        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")));
 }