public static byte[] Serialize(this SlimBlock slimBlock) { // These buffers will be concatenated to create the full block; List <byte[]> parts = new List <byte[]>(); // Block header parts.Add(slimBlock.SlimBlockHeader.Data); // Number of tx in the block byte[] nTransactions = new byte[1]; // this must be VarInt, todo: use real varint var txCount = slimBlock.IsProofOfStake ? slimBlock.PayloadTransactions.Count + 2 : slimBlock.PayloadTransactions.Count + 1; if (txCount > 252) { throw new InvalidOperationException($"Value of {txCount} needs a real VarInt"); } nTransactions[0] = (byte)txCount; parts.Add(nTransactions); // Transactions var coinbaseTxBytes = slimBlock.CoinbaseTransaction.ToBytes(); parts.Add(coinbaseTxBytes); if (slimBlock.IsProofOfStake) { Debug.Assert(slimBlock.CoinstakeTransaction.IsCoinstake()); var coinstakeTxBytes = slimBlock.CoinstakeTransaction.ToBytes(); parts.Add(coinstakeTxBytes); } foreach (var payloadTransaction in slimBlock.PayloadTransactions) { parts.Add(payloadTransaction.ToBytes()); } // Block signature as VarString. First byte of VarString is length as VarInt if (slimBlock.IsProofOfStake) { // 252 (0xfc) is the max for a 1-byte VarInt, and we assume the signature is not longer Debug.Assert(slimBlock.SignatureBytes.Length <= 252); parts.Add(slimBlock.SignatureBytes); // SignatureBytes is written by the BlockSignature ReadWrite method. This should already be in VarString format. //byte[] signature = new byte[1 + slimBlock.SignatureBytes.Length]; //signature[0] = (byte)slimBlock.SignatureBytes.Length; //Buffer.BlockCopy(slimBlock.SignatureBytes, 0, signature, 1, slimBlock.SignatureBytes.Length); //parts.Add(signature); } else { parts.Add(slimBlock.SignatureBytes); } var blockBytes = ByteArrays.Concatenate(parts.ToArray()); return(blockBytes); }
public static void AddPoSCoinbaseTransaction(this SlimBlock slimBlock) { slimBlock.CoinbaseTransaction = C.Network.CreateTransaction(); slimBlock.CoinbaseTransaction.Inputs.Add(new TxIn { ScriptSig = new Script(Op.GetPushOp(slimBlock.Height)) + OpcodeType.OP_0, WitScript = CoinbaseWitScript }); slimBlock.CoinbaseTransaction.Outputs.Add(new TxOut(0, new Script())); }
public static void CreateWitnessCommitment(this SlimBlock slimBlock) { if (slimBlock.CoinbaseTransaction == null || slimBlock.PayloadTransactions == null) { throw new ArgumentException(); } if (slimBlock.IsProofOfStake && slimBlock.CoinstakeTransaction == null) { throw new ArgumentException(); } if (slimBlock.CoinbaseTransaction.Inputs[0].WitScript == WitScript.Empty) { throw new ArgumentException(); } byte[] witnessRootHash; if (slimBlock.IsProofOfStake) { List <Transaction> coinStakeAndPayload = new List <Transaction>(slimBlock.PayloadTransactions); coinStakeAndPayload.Insert(0, slimBlock.CoinstakeTransaction); witnessRootHash = BlockWitnessMerkleRoot(coinStakeAndPayload); } else { witnessRootHash = BlockWitnessMerkleRoot(slimBlock.PayloadTransactions); } // // Coinbase's input's witness must consist of a single 32-byte array for the witness reserved value. byte[] witnessReservedValue = new byte[32]; byte[] dataToHash = new byte[64]; // witness root hash << witness reserved value Buffer.BlockCopy(witnessRootHash, 0, dataToHash, 0, 32); Buffer.BlockCopy(witnessReservedValue, 0, dataToHash, 32, 32); // 32-byte - Commitment hash: Double-SHA256(witness root hash|witness reserved value) byte[] commitmentHash = Hashes.DoubleSHA256(dataToHash).ToBytes(); // The commitment is recorded in a scriptPubKey of the coinbase transaction. var coinbaseScriptPubKeyFiledBytes = new byte[38]; // It must be at least 38 bytes, with the first 6-byte of 0x6a24aa21a9ed. coinbaseScriptPubKeyFiledBytes[0] = 0x6a; // OP_RETURN (0x6a) coinbaseScriptPubKeyFiledBytes[1] = 0x24; // Push the following 36 bytes (0x24) coinbaseScriptPubKeyFiledBytes[2] = 0xaa; // Commitment header (0xaa21a9ed) coinbaseScriptPubKeyFiledBytes[3] = 0x21; coinbaseScriptPubKeyFiledBytes[4] = 0xa9; coinbaseScriptPubKeyFiledBytes[5] = 0xed; Buffer.BlockCopy(commitmentHash, 0, coinbaseScriptPubKeyFiledBytes, 6, 32); // Write the coinbase commitment to a ScriptPubKey structure. var txOut = new TxOut(Money.Zero, new Script(coinbaseScriptPubKeyFiledBytes)); // If there are more than one scriptPubKey matching the pattern, the one with highest output index is assumed to be the commitment. slimBlock.CoinbaseTransaction.Outputs.Add(txOut); }
public static void UpdateHeaderHashMerkleRoot(this SlimBlock slimBlock) { var txHashes = new List <byte[]>(); txHashes.Add(slimBlock.CoinbaseTransaction.GetHash().ToBytes()); if (slimBlock.IsProofOfStake) { txHashes.Add(slimBlock.CoinstakeTransaction.GetHash().ToBytes()); } foreach (var transaction in slimBlock.PayloadTransactions) { txHashes.Add(transaction.GetHash().ToBytes()); } var hashMerkleRoot = MerkleRoot.Build(txHashes); slimBlock.SlimBlockHeader.MerkleRoot = hashMerkleRoot; slimBlock.SlimBlockHeader.Data = slimBlock.SlimBlockHeader.SerializeTo80Bytes(); }
public static void AddPoWCoinbaseTransaction(this SlimBlock slimBlock, Script scriptPubkey, Money value, long extraNonce) { if (slimBlock.IsProofOfStake || slimBlock.Height == 0) { throw new InvalidOperationException(); } // txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)); var heightScriptBytes = new Script(Op.GetPushOp(slimBlock.Height)).ToBytes(); var extraNonceScriptBytes = new CScriptNum(extraNonce).getvch(); var scriptSigBytes = new byte[heightScriptBytes.Length + extraNonceScriptBytes.Length]; Buffer.BlockCopy(heightScriptBytes, 0, scriptSigBytes, 0, heightScriptBytes.Length); Buffer.BlockCopy(extraNonceScriptBytes, 0, scriptSigBytes, heightScriptBytes.Length, extraNonceScriptBytes.Length); slimBlock.CoinbaseTransaction = C.Network.CreateTransaction(); slimBlock.CoinbaseTransaction.Inputs.Add(new TxIn { ScriptSig = new Script(scriptSigBytes), WitScript = CoinbaseWitScript }); slimBlock.CoinbaseTransaction.Outputs.Add(new TxOut(value, scriptPubkey)); }
public static void SetPayloadTransactions(this SlimBlock slimBlock, IEnumerable <Transaction> transactions) { slimBlock.PayloadTransactions = transactions.ToList(); }