public async void Start(IObserver <ByteString> observer)
            {
                try
                {
                    ByteString cursor = parent.from;

                    while (!cancel.Token.IsCancellationRequested)
                    {
                        IReadOnlyList <ByteString> result = await parent.query(cursor);

                        ByteString lastRecord = null;
                        foreach (ByteString record in result)
                        {
                            observer.OnNext(record);
                            lastRecord = record;
                        }

                        if (lastRecord != null)
                        {
                            cursor = new ByteString(MessageSerializer.ComputeHash(lastRecord.ToByteArray()));
                        }

                        await Task.Delay(TimeSpan.FromSeconds(0.2));
                    }

                    observer.OnCompleted();
                }
                catch (Exception exception)
                {
                    observer.OnError(exception);
                }
            }
        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();
            }
        }
        private async Task SignAndSubmit(byte[] serializedMutation)
        {
            byte[] hash = MessageSerializer.ComputeHash(serializedMutation);

            byte[] signature = tedChainKey.Sign(new NBitcoin.uint256(hash)).ToDER();

            HttpClient client = new HttpClient();
            JObject    json   = JObject.FromObject(new
            {
                mutation   = new ByteString(serializedMutation).ToString(),
                signatures = new[]
                {
                    new
                    {
                        pub_key   = new ByteString(tedChainKey.PubKey.ToBytes()).ToString(),
                        signature = new ByteString(signature).ToString()
                    }
                }
            });

            StringContent content = new StringContent(json.ToString());

            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            HttpResponseMessage response = await client.PostAsync(new Uri(tedChainUri, "submit"), content);

            response.EnsureSuccessStatusCode();
        }
Example #4
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();
            }
        }
Example #6
0
        public async Task <ByteString> PostTransaction(ByteString rawMutation, IReadOnlyList <SignatureEvidence> authentication)
        {
            Mutation mutation;

            try
            {
                // Verify that the mutation can be deserialized
                mutation = MessageSerializer.DeserializeMutation(rawMutation);
            }
            catch (InvalidProtocolBufferException)
            {
                throw new TransactionInvalidException("InvalidMutation");
            }

            ParsedMutation parsedMutation = ParsedMutation.Parse(mutation);

            IReadOnlyDictionary <AccountKey, AccountStatus> accounts = await ValidateMutation(mutation, parsedMutation);

            ValidateAuthentication(authentication, MessageSerializer.ComputeHash(rawMutation.ToByteArray()));

            DateTime date = DateTime.UtcNow;

            IList <Mutation> generatedMutations = await this.validator.Validate(parsedMutation, authentication, accounts);

            TransactionMetadata metadata = new TransactionMetadata(authentication);

            byte[] rawMetadata = SerializeMetadata(metadata);

            Transaction transaction = new Transaction(rawMutation, date, new ByteString(rawMetadata));

            byte[] serializedTransaction = MessageSerializer.SerializeTransaction(transaction);

            List <ByteString> transactions = new List <ByteString>()
            {
                new ByteString(serializedTransaction)
            };

            transactions.AddRange(await Task.WhenAll(generatedMutations.Select(async generatedMutation =>
            {
                await ValidateMutation(generatedMutation, ParsedMutation.Parse(generatedMutation));
                Transaction generatedTransaction = new Transaction(new ByteString(MessageSerializer.SerializeMutation(generatedMutation)), date, ByteString.Empty);
                return(new ByteString(MessageSerializer.SerializeTransaction(generatedTransaction)));
            })));

            try
            {
                await this.store.AddTransactions(transactions);
            }
            catch (ConcurrentMutationException)
            {
                throw new TransactionInvalidException("OptimisticConcurrency");
            }

            return(new ByteString(MessageSerializer.ComputeHash(serializedTransaction)));
        }
        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())));
        }
        private async Task <ByteString> AddTransaction(params Record[] records)
        {
            Mutation    mutation           = new Mutation(ByteString.Parse("0123"), records, ByteString.Parse("4567"));
            ByteString  serializedMutation = new ByteString(MessageSerializer.SerializeMutation(mutation));
            Transaction transaction        = new Transaction(
                serializedMutation,
                new DateTime(1, 2, 3, 4, 5, 6),
                ByteString.Parse("abcdef"));

            await this.Store.AddTransactions(new[] { new ByteString(MessageSerializer.SerializeTransaction(transaction)) });

            return(new ByteString(MessageSerializer.ComputeHash(serializedMutation.ToByteArray())));
        }
        private ByteString AddRecord(string key)
        {
            Mutation mutation = new Mutation(
                ByteString.Empty,
                new Record[] { new Record(new ByteString(Encoding.UTF8.GetBytes(key)), ByteString.Empty, ByteString.Empty) },
                ByteString.Empty);

            Transaction transaction = new Transaction(
                new ByteString(MessageSerializer.SerializeMutation(mutation)),
                new DateTime(),
                ByteString.Empty);

            this.transactions.Add(new ByteString(MessageSerializer.SerializeTransaction(transaction)));

            return(new ByteString(MessageSerializer.ComputeHash(MessageSerializer.SerializeTransaction(transaction))));
        }
Example #10
0
        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);
        }
        public static GlobalSettings CreateGlobalSettings(IServiceProvider serviceProvider)
        {
            string instanceSeed = serviceProvider.GetService <IConfiguration>().GetSection("validator_mode")["instance_seed"];

            ByteString validNamespace;

            if (string.IsNullOrEmpty(instanceSeed))
            {
                serviceProvider.GetService <ILogger>().LogWarning(
                    $"No instance seed is configured, this instance is not able to validate transactions");
                validNamespace = null;
            }
            else
            {
                validNamespace = new ByteString(MessageSerializer.ComputeHash(Encoding.UTF8.GetBytes(instanceSeed)).Take(8).ToArray());
            }

            return(new GlobalSettings(validNamespace));
        }
        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);
                }
            }
        }
        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()
            }));
        }
Example #14
0
        public async Task Subscribe(CancellationToken cancel)
        {
            byte[] buffer = new byte[1024 * 1024];
            ArraySegment <byte> segment = new ArraySegment <byte>(buffer);

            ByteString currentRecord = await this.store.GetLastTransaction();

            while (!cancel.IsCancellationRequested)
            {
                try
                {
                    ClientWebSocket socket = new ClientWebSocket();

                    this.endpoint.Query = string.Format("from={0}", currentRecord.ToString());

                    logger.LogInformation("Connecting to {0}", this.endpoint.Uri);

                    await socket.ConnectAsync(this.endpoint.Uri, cancel);

                    while (true)
                    {
                        WebSocketReceiveResult result = await socket.ReceiveAsync(segment, cancel);

                        if (result.MessageType == WebSocketMessageType.Close)
                        {
                            break;
                        }

                        ByteString record = new ByteString(buffer.Take(result.Count));
                        await store.AddTransactions(new[] { record });

                        currentRecord = new ByteString(MessageSerializer.ComputeHash(record.ToByteArray()));
                    }
                }
                catch (Exception exception)
                {
                    logger.LogError("Error in the stream subscriber: {0}", exception.ToString());
                }

                await Task.Delay(TimeSpan.FromMinutes(1));
            }
        }
        private async Task <ByteString> AddRecords(ByteString version, ByteString value, params string[] keys)
        {
            Mutation mutation = new Mutation(
                ByteString.Empty,
                keys.Select(key => new Record(
                                new ByteString(Encoding.UTF8.GetBytes(key)),
                                value,
                                version)),
                ByteString.Empty);

            byte[] serializedMutation = MessageSerializer.SerializeMutation(mutation);

            Transaction transaction = new Transaction(
                new ByteString(MessageSerializer.SerializeMutation(mutation)),
                new DateTime(),
                ByteString.Empty);

            await Engine.AddTransactions(new[] { new ByteString(MessageSerializer.SerializeTransaction(transaction)) });

            return(new ByteString(MessageSerializer.ComputeHash(serializedMutation)));
        }
        public async Task AddTransaction_MultipleTransactionsSuccess()
        {
            IList <Record> records1 = new Record[]
            {
                new Record(binaryData[0], binaryData[1], ByteString.Empty),
                new Record(binaryData[2], binaryData[3], ByteString.Empty),
            };

            ByteString mutation1     = new ByteString(MessageSerializer.SerializeMutation(new Mutation(ByteString.Empty, records1, ByteString.Empty)));
            ByteString mutationHash1 = new ByteString(MessageSerializer.ComputeHash(mutation1.ToByteArray()));

            IList <Record> records2 = new Record[]
            {
                new Record(binaryData[2], binaryData[5], mutationHash1),
                new Record(binaryData[6], binaryData[7], ByteString.Empty),
            };

            ByteString mutation2     = new ByteString(MessageSerializer.SerializeMutation(new Mutation(ByteString.Empty, records2, ByteString.Empty)));
            ByteString mutationHash2 = new ByteString(MessageSerializer.ComputeHash(mutation2.ToByteArray()));

            // Submit both transactions at once
            await this.Store.AddTransactions(new[]
            {
                new ByteString(MessageSerializer.SerializeTransaction(new Transaction(mutation1, new DateTime(), ByteString.Empty))),
                new ByteString(MessageSerializer.SerializeTransaction(new Transaction(mutation2, new DateTime(), ByteString.Empty)))
            });

            IReadOnlyList <Record> result1 = await this.Store.GetRecords(new[] { binaryData[0] });

            IReadOnlyList <Record> result2 = await this.Store.GetRecords(new[] { binaryData[2] });

            IReadOnlyList <Record> result3 = await this.Store.GetRecords(new[] { binaryData[6] });

            AssertRecord(result1[0], binaryData[0], binaryData[1], mutationHash1);
            AssertRecord(result2[0], binaryData[2], binaryData[5], mutationHash2);
            AssertRecord(result3[0], binaryData[6], binaryData[7], mutationHash2);
        }
Example #17
0
        public async Task Subscribe(CancellationToken cancel)
        {
            byte[] buffer = new byte[1024 * 1024];
            ArraySegment <byte> segment = new ArraySegment <byte>(buffer);

            IServiceScopeFactory scopeFactory = services.GetService <IServiceScopeFactory>();
            ILogger logger = services.GetRequiredService <ILogger>();

            while (!cancel.IsCancellationRequested)
            {
                try
                {
                    using (IServiceScope scope = scopeFactory.CreateScope())
                    {
                        IStorageEngine storageEngine = scope.ServiceProvider.GetRequiredService <IStorageEngine>();
                        await storageEngine.Initialize();

                        ByteString currentRecord = await storageEngine.GetLastTransaction();

                        ClientWebSocket socket = new ClientWebSocket();

                        this.endpoint.Query = string.Format("from={0}", currentRecord.ToString());

                        logger.LogInformation("Connecting to {0}", this.endpoint.Uri);

                        await socket.ConnectAsync(this.endpoint.Uri, cancel);

                        while (true)
                        {
                            ByteString transaction;

                            using (MemoryStream stream = new MemoryStream(1024))
                            {
                                WebSocketReceiveResult result;

                                do
                                {
                                    result = await socket.ReceiveAsync(segment, cancel);

                                    if (result.MessageType == WebSocketMessageType.Close)
                                    {
                                        break;
                                    }

                                    stream.Write(segment.Array, segment.Offset, result.Count);
                                } while (!result.EndOfMessage);

                                stream.Seek(0, SeekOrigin.Begin);

                                using (BinaryReader reader = new BinaryReader(stream))
                                {
                                    transaction = new ByteString(reader.ReadBytes((int)stream.Length));
                                }
                            }

                            await storageEngine.AddTransactions(new[] { transaction });

                            currentRecord = new ByteString(MessageSerializer.ComputeHash(transaction.ToByteArray()));
                        }
                    }
                }
                catch (Exception exception)
                {
                    logger.LogError("Error in the stream subscriber: {0}", exception.ToString());
                }

                await Task.Delay(TimeSpan.FromMinutes(1));
            }
        }
 private static ByteString GetMutationHash(ByteString transaction)
 {
     return(new ByteString(
                MessageSerializer.ComputeHash(MessageSerializer.DeserializeTransaction(transaction).Mutation.ToByteArray())));
 }
Example #19
0
        public async Task <ActionResult> Post()
        {
            if (validator == null)
            {
                return(CreateErrorResponse("ValidationDisabled"));
            }

            JObject body;

            try
            {
                string bodyContent;
                using (StreamReader streamReader = new StreamReader(Request.Body))
                    bodyContent = await streamReader.ReadToEndAsync();

                body = JObject.Parse(bodyContent);
            }
            catch (JsonReaderException)
            {
                return(BadRequest());
            }

            ByteString parsedMutation;
            List <SignatureEvidence> authentication = new List <SignatureEvidence>();

            if (!(body["mutation"] is JValue && body["signatures"] is JArray))
            {
                return(BadRequest());
            }

            try
            {
                parsedMutation = ByteString.Parse((string)body["mutation"]);

                foreach (JToken signatureItem in body["signatures"])
                {
                    JObject evidence = signatureItem as JObject;
                    if (!(evidence != null && evidence["pub_key"] is JValue && evidence["signature"] is JValue))
                    {
                        return(BadRequest());
                    }

                    authentication.Add(new SignatureEvidence(
                                           ByteString.Parse((string)evidence["pub_key"]),
                                           ByteString.Parse((string)evidence["signature"])));
                }
            }
            catch (FormatException)
            {
                return(BadRequest());
            }

            ByteString transactionId;

            try
            {
                transactionId = await validator.PostTransaction(parsedMutation, authentication);
            }
            catch (TransactionInvalidException exception)
            {
                logger.LogInformation("Rejected transaction: {0}", exception.Message);

                return(CreateErrorResponse(exception.Reason));
            }

            logger.LogInformation("Validated transaction {0}", transactionId.ToString());

            return(Json(new
            {
                transaction_hash = transactionId.ToString(),
                mutation_hash = new ByteString(MessageSerializer.ComputeHash(parsedMutation.ToByteArray())).ToString()
            }));
        }
Example #20
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;
            }
        }
Example #21
0
        public async Task <ByteString> PostTransaction(ByteString rawMutation, IReadOnlyList <SignatureEvidence> authentication)
        {
            Mutation mutation;

            try
            {
                // Verify that the mutation can be deserialized
                mutation = MessageSerializer.DeserializeMutation(rawMutation);
            }
            catch (InvalidProtocolBufferException)
            {
                throw new TransactionInvalidException("InvalidMutation");
            }

            if (!mutation.Namespace.Equals(this.Namespace))
            {
                throw new TransactionInvalidException("InvalidNamespace");
            }

            if (mutation.Records.Count == 0)
            {
                throw new TransactionInvalidException("InvalidMutation");
            }

            if (mutation.Records.Any(record => record.Key.Value.Count > MaxKeySize))
            {
                throw new TransactionInvalidException("InvalidMutation");
            }

            ValidateAuthentication(authentication, MessageSerializer.ComputeHash(rawMutation.ToByteArray()));

            ParsedMutation parsedMutation = ParsedMutation.Parse(mutation);

            // All assets must have an overall zero balance

            IReadOnlyDictionary <AccountKey, AccountStatus> accounts =
                await this.store.GetAccounts(parsedMutation.AccountMutations.Select(entry => entry.AccountKey));

            var groups = parsedMutation.AccountMutations
                         .GroupBy(account => account.AccountKey.Asset.FullPath)
                         .Select(group => group.Sum(entry => entry.Balance - accounts[entry.AccountKey].Balance));

            if (groups.Any(group => group != 0))
            {
                throw new TransactionInvalidException("UnbalancedTransaction");
            }

            DateTime date = DateTime.UtcNow;

            await this.validator.Validate(parsedMutation, authentication, accounts);

            TransactionMetadata metadata = new TransactionMetadata(authentication);

            byte[] rawMetadata = SerializeMetadata(metadata);

            Transaction transaction = new Transaction(rawMutation, date, new ByteString(rawMetadata));

            byte[] serializedTransaction = MessageSerializer.SerializeTransaction(transaction);

            try
            {
                await this.store.AddTransactions(new[] { new ByteString(serializedTransaction) });
            }
            catch (ConcurrentMutationException)
            {
                throw new TransactionInvalidException("OptimisticConcurrency");
            }

            return(new ByteString(MessageSerializer.ComputeHash(serializedTransaction)));
        }
        //TEMP
        public async Task <int> UpdateTransactionsDate()
        {
            using (SqlTransaction context = Connection.BeginTransaction(IsolationLevel.Snapshot))
            {
                try
                {
                    var transactionsData = await ExecuteQuery(
                        @"IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS
                            WHERE TABLE_NAME = 'Transactions' AND COLUMN_NAME = 'Created')
                        BEGIN
                            ALTER TABLE [Openchain].[Transactions] ADD 
                                Created datetime
                        END
                        SELECT [RawData] 
                        FROM [Openchain].[Transactions] 
                        WHERE [Created] IS NULL",
                        reader => new ByteString((byte[])reader[0]),
                        new Dictionary <string, object>(),
                        context);

                    var transactions = transactionsData.Select(x => new ExtTransaction(x)).ToList();

                    foreach (ExtTransaction extTransaction in transactions)
                    {
                        byte[] rawTransactionBuffer = extTransaction.TransactionData.ToByteArray();
                        byte[] transactionHash      = MessageSerializer.ComputeHash(rawTransactionBuffer);
                        byte[] mutationHash         = MessageSerializer.ComputeHash(extTransaction.Transaction.Mutation.ToByteArray());

                        IReadOnlyList <object> conflicts = await ExecuteQuery(
                            "UPDATE [Openchain].[Transactions] SET[Created] = @created WHERE[MutationHash] = @mutationHash  AND [TransactionHash] = @transactionHash",
                            reader => reader
                            ,
                            new Dictionary <string, object>()
                        {
                            ["mutationHash"]    = mutationHash,
                            ["transactionHash"] = transactionHash,
                            ["created"]         = extTransaction.Transaction.Timestamp
                        },
                            context);

                        if (conflicts.Count > 0)
                        {
                            throw new Exception();
                        }
                    }

                    context.Commit();

                    return(transactions.Count);
                }
                catch (Exception ex)
                {
                    if (!(ex is ConcurrentMutationException))
                    {
                        var excep = ex;
                    }

                    throw;
                }
            }
        }