public async Task SaveAndLoadUint256_ToDatabase_ShouldBeStoreAsBigEndian(string data) { var binary = Encoders.Hex.DecodeData(data); // Big endian var parsed = uint256.Parse(data); var entity = new Ztm.Data.Entity.Contexts.Main.Block { Height = 0, Hash = parsed, Version = 0, Bits = new Target(0), Nonce = 0, Time = DateTime.UtcNow, MerkleRoot = uint256.Zero }; try { // Store to database. await this.subject.Blocks.AddAsync(entity, CancellationToken.None); await this.subject.SaveChangesAsync(); // Assert Endianness. using (var command = this.subject.Database.GetDbConnection().CreateCommand()) { command.CommandText = "SELECT \"Hash\" FROM \"Blocks\" LIMIT 1;"; this.subject.Database.OpenConnection(); using (var result = command.ExecuteReader()) { Assert.True(result.Read(), "No available data"); var record = (IDataRecord)result; var hash = record[0] as byte[]; // Get raw data. Assert.Equal(binary, hash); } } // Get uint256 back. var block = await this.subject.Blocks.FirstOrDefaultAsync(); Assert.NotNull(block); Assert.Equal(parsed, block.Hash); } finally { this.subject.Database.ExecuteSqlCommand("TRUNCATE TABLE \"Blocks\" CASCADE"); } }
Block ToDomain(Ztm.Data.Entity.Contexts.Main.Block data, Ztm.Data.Entity.Contexts.Main.Block previous = null) { var block = Block.CreateBlock(this.zcoinNetwork); // Block properties. block.Header.Version = data.Version; block.Header.HashMerkleRoot = data.MerkleRoot; block.Header.BlockTime = DateTime.SpecifyKind(data.Time, DateTimeKind.Utc); block.Header.Bits = data.Bits; block.Header.Nonce = (uint)data.Nonce; if (data.MtpVersion != null) { if (!block.Header.IsMtp()) { throw new ArgumentException( "The data is MTP-enabled but the consensus not activated for this data.", nameof(data) ); } if (data.MtpHashValue == null || data.Reserved1 == null || data.Reserved2 == null) { throw new ArgumentException( "The data has MtpVersion but some other required properties is null.", nameof(data) ); } block.Header.SetMtpVersion(data.MtpVersion.Value); block.Header.SetMtpHashValue(data.MtpHashValue); block.Header.SetReserved1(data.Reserved1); block.Header.SetReserved2(data.Reserved2); } if (previous != null) { block.Header.HashPrevBlock = previous.Hash; } block.Transactions = data.Transactions .Select(e => ToDomain(e.Transaction)) .Cast <Transaction>() .ToList(); return(block); }
public async Task SaveAndLoadUint256_ToDatabase_ShouldBeStoreAsBigEndian(string data) { using (var db = this.fixture.CreateDbContext()) { var binary = Encoders.Hex.DecodeData(data); // Big endian var parsed = uint256.Parse(data); var entity = new Ztm.Data.Entity.Contexts.Main.Block { Height = 0, Hash = parsed, Version = 0, Bits = new Target(0), Nonce = 0, Time = DateTime.UtcNow, MerkleRoot = uint256.Zero }; // Store to database. await db.Blocks.AddAsync(entity, CancellationToken.None); await db.SaveChangesAsync(); // Assert Endianness. var blockHashes = this.fixture.ExecuteSql("SELECT \"Hash\" FROM \"Blocks\" LIMIT 1;") .Select(r => r[0] as byte[]); var hash = Assert.Single(blockHashes); Assert.Equal(binary, hash); // Get uint256 back. var block = await db.Blocks.FirstOrDefaultAsync(); Assert.NotNull(block); Assert.Equal(parsed, block.Hash); } }
async Task StoreBlockAsync(Block block, int height) { var entity = new Ztm.Data.Entity.Contexts.Main.Block() { Height = height, Hash = block.GetHash(), Version = block.Header.Version, Bits = block.Header.Bits, Nonce = block.Header.Nonce, Time = block.Header.BlockTime.UtcDateTime, MerkleRoot = block.Header.HashMerkleRoot, }; for (var i = 0; i < block.Transactions.Count; i++) { var tx = new Ztm.Data.Entity.Contexts.Main.Transaction() { Hash = block.Transactions[i].GetHash(), Version = block.Transactions[i].Version, LockTime = block.Transactions[i].LockTime, }; for (var j = 0; j < block.Transactions[i].Inputs.Count; j++) { var input = block.Transactions[i].Inputs[j]; tx.Inputs.Add(new Ztm.Data.Entity.Contexts.Main.Input() { TransactionHash = tx.Hash, Index = j, OutputHash = input.PrevOut.Hash, OutputIndex = input.PrevOut.N, Script = input.ScriptSig, Sequence = input.Sequence, }); } for (var j = 0; j < block.Transactions[i].Outputs.Count; j++) { var output = block.Transactions[i].Outputs[j]; tx.Outputs.Add(new Ztm.Data.Entity.Contexts.Main.Output() { TransactionHash = tx.Hash, Index = j, Value = output.Value, Script = output.ScriptPubKey, }); } entity.Transactions.Add(new Ztm.Data.Entity.Contexts.Main.BlockTransaction() { BlockHash = entity.Hash, TransactionHash = tx.Hash, Index = i, Transaction = tx, }); } using (var db = this.db.CreateDbContext()) { await db.Blocks.AddAsync(entity); await db.SaveChangesAsync(); } }
Ztm.Data.Entity.Contexts.Main.Block ToEntity(Block block, int height) { var header = block.Header; var entity = new Ztm.Data.Entity.Contexts.Main.Block() { Height = height, Hash = block.GetHash(), Version = header.Version, Bits = header.Bits, Nonce = header.Nonce, Time = header.BlockTime.UtcDateTime, MerkleRoot = header.HashMerkleRoot }; if (header.IsMtp()) { if (header.GetMtpHashValue() == null || header.GetReserved1() == null || header.GetReserved2() == null) { throw new ArgumentException( "Block is MTP-enabled but some required fields is null.", nameof(block) ); } entity.MtpVersion = header.GetMtpVersion(); entity.MtpHashValue = header.GetMtpHashValue(); entity.Reserved1 = header.GetReserved1(); entity.Reserved2 = header.GetReserved2(); } // Transactions. var transactions = new Dictionary <uint256, Ztm.Data.Entity.Contexts.Main.Transaction>(); for (int i = 0; i < block.Transactions.Count; i++) { Ztm.Data.Entity.Contexts.Main.Transaction tx; block.Transactions[i].PrecomputeHash(invalidateExisting: true, lazily: false); var hash = block.Transactions[i].GetHash(); var blockTx = new Ztm.Data.Entity.Contexts.Main.BlockTransaction() { BlockHash = block.GetHash(), TransactionHash = hash, Index = i, Block = entity }; if (!transactions.TryGetValue(hash, out tx)) { tx = ToEntity(block.Transactions[i]); transactions.Add(hash, tx); blockTx.Transaction = tx; tx.Blocks.Add(blockTx); } entity.Transactions.Add(blockTx); } return(entity); }