/**
* Novacoin classes library
* Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
using System;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Diagnostics.Contracts;
namespace Novacoin
{
///
/// Represents the block. Block consists of header, transaction array and header signature.
///
public class CBlock
{
///
/// Block header.
///
public CBlockHeader header;
///
/// Transactions array.
///
public CTransaction[] vtx;
///
/// Block header signature.
///
public byte[] signature = new byte[0];
public CBlock(CBlock b)
{
header = new CBlockHeader(b.header);
vtx = new CTransaction[b.vtx.Length];
for (int i = 0; i < b.vtx.Length; i++)
{
vtx[i] = new CTransaction(b.vtx[i]);
}
b.signature.CopyTo(signature, 0);
}
///
/// Parse byte sequence and initialize new block instance
///
///
public CBlock (byte[] blockBytes)
{
ByteQueue wBytes = new ByteQueue(blockBytes);
// Fill the block header fields
header = new CBlockHeader(wBytes.Get(80));
// Parse transactions list
vtx = CTransaction.ReadTransactionsList(ref wBytes);
// Read block signature
signature = wBytes.Get((int)wBytes.GetVarInt());
}
public CBlock()
{
// Initialize empty array of transactions. Please note that such
// configuration is not valid real block since it has to provide
// at least one transaction.
vtx = new CTransaction[0];
}
///
/// Is this a Proof-of-Stake block?
///
public bool IsProofOfStake
{
get
{
return (vtx.Length > 1 && vtx[1].IsCoinStake);
}
}
///
/// Was this signed correctly?
///
public bool SignatureOK
{
get
{
if (IsProofOfStake)
{
if (signature.Length == 0)
{
return false; // No signature
}
txnouttype whichType;
IList solutions;
if (!ScriptCode.Solver(vtx[1].vout[1].scriptPubKey, out whichType, out solutions))
{
return false; // No solutions found
}
if (whichType == txnouttype.TX_PUBKEY)
{
CPubKey pubkey;
try
{
pubkey = new CPubKey(solutions[0]);
}
catch (Exception)
{
return false; // Error while loading public key
}
return pubkey.VerifySignature(header.Hash, signature);
}
}
else
{
// Proof-of-Work blocks have no signature
return true;
}
return false;
}
}
///
/// Get instance as sequence of bytes
///
/// Byte sequence
public static implicit operator byte[] (CBlock b)
{
var r = new List();
r.AddRange((byte[])b.header);
r.AddRange(VarInt.EncodeVarInt(b.vtx.LongLength)); // transactions count
foreach (var tx in b.vtx)
{
r.AddRange((byte[])tx);
}
r.AddRange(VarInt.EncodeVarInt(b.signature.LongLength));
r.AddRange(b.signature);
return r.ToArray();
}
///
/// Serialized size
///
public int Size
{
get
{
int nSize = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
foreach (var tx in vtx)
{
nSize += tx.Size;
}
nSize += VarInt.GetEncodedSize(signature.Length) + signature.Length;
return nSize;
}
}
///
/// Get transaction offset inside block.
///
/// Transaction index.
/// Offset in bytes from the beginning of block header.
public int GetTxOffset(int nTx)
{
Contract.Requires(nTx >= 0 && nTx < vtx.Length, "Transaction index you've specified is incorrect.");
int nOffset = 80 + VarInt.GetEncodedSize(vtx.Length); // CBlockHeader + NumTx
for (int i = 0; i < nTx; i++)
{
nOffset += vtx[i].Size;
}
return nOffset;
}
///
/// Merkle root
///
public Hash256 hashMerkleRoot
{
get {
var merkleTree = new List();
foreach (var tx in vtx)
{
merkleTree.AddRange(Hash256.ComputeRaw256(tx));
}
int levelOffset = 0;
for (int nLevelSize = vtx.Length; nLevelSize > 1; nLevelSize = (nLevelSize + 1) / 2)
{
for (int nLeft = 0; nLeft < nLevelSize; nLeft += 2)
{
int nRight = Math.Min(nLeft + 1, nLevelSize - 1);
var left = merkleTree.GetRange((levelOffset + nLeft) * 32, 32).ToArray();
var right = merkleTree.GetRange((levelOffset + nRight) * 32, 32).ToArray();
merkleTree.AddRange(Hash256.ComputeRaw256(ref left, ref right));
}
levelOffset += nLevelSize;
}
return (merkleTree.Count == 0) ? new Hash256() : new Hash256(merkleTree.GetRange(merkleTree.Count-32, 32).ToArray());
}
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendFormat("CBlock(\n header={0},\n", header.ToString());
foreach(var tx in vtx)
{
sb.AppendFormat("{0}", tx.ToString());
}
if (IsProofOfStake)
{
sb.AppendFormat(", signature={0}, signatureOK={1}\n", Interop.ToHex(signature), SignatureOK);
}
sb.Append(")");
return sb.ToString();
}
}
}