Skip to content

Instantly share code, notes, and snippets.

@Coding-Enthusiast
Created February 23, 2021 08:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Coding-Enthusiast/ce0bcb3baa565f7dc88d94dc96406f2a to your computer and use it in GitHub Desktop.
Save Coding-Enthusiast/ce0bcb3baa565f7dc88d94dc96406f2a to your computer and use it in GitHub Desktop.
Testing coinbase maturity on RegTest to be used in Bitcoin.Net CoinbaseQueue class
// Copyright (c) 2021 Autarkysoft
// Distributed under the MIT software license.
// See http://www.opensource.org/licenses/mit-license.php.
// This is using Bitcoin.Net 0.9.0
// Install from nuget using `Install-Package Autarkysoft.Bitcoin -Version 0.9.0`
using Autarkysoft.Bitcoin;
using Autarkysoft.Bitcoin.Blockchain.Blocks;
using Autarkysoft.Bitcoin.Blockchain.Scripts;
using Autarkysoft.Bitcoin.Blockchain.Transactions;
using Autarkysoft.Bitcoin.Cryptography.Asymmetric.KeyPairs;
using Autarkysoft.Bitcoin.Encoders;
using System;
using System.Numerics;
// There is a bug somewhere either in my understanding or it core's reporting errors while rejecting a block
namespace BitcoinCoreMiningBug
{
class Program
{
static void Main(string[] args)
{
// This is a test to figure out the absolute minimum value to set for our coinbase queue used by UTXO database
// ie. the coinbase maturity which is 100 blocks.
// However, while testing bitcoin core kept rejecting the block with a "high-hash" error message while the
// has was clearning NOT higher than the target.
// More discussion here: https://bitcointalk.org/index.php?topic=5319224.0
Start();
}
private static void Start()
{
ReadPrivateKey("Enter private key: ", out PrivateKey key);
ReadData("Enter tx to spend: ", out byte[] prvTxBa);
var prvTx = new Transaction();
bool b = prvTx.TryDeserialize(new FastStreamReader(prvTxBa), out string error);
Assert(b);
var coinbase = new Transaction()
{
Version = 2,
LockTime = 0,
TxInList = new TxIn[]
{
new TxIn(new byte[32], uint.MaxValue, null, uint.MaxValue)
},
TxOutList = new TxOut[]
{
new TxOut()
}
};
var tx = new Transaction()
{
Version = 2,
LockTime = 0,
TxInList = new TxIn[]
{
new TxIn(prvTx.GetTransactionHash(), 0, null, uint.MaxValue)
},
TxOutList = new TxOut[]
{
new TxOut()
}
};
key.Sign(tx, prvTx, 0);
while (true)
{
ReadInt("Enter block height to be mined: ", out int height);
// We have to manually pad to avoid invalid coinbase script length (<2).
// This issue is solved in Bitcoin.Net 0.10.0
byte[] padding = height < 17 ? new byte[] { 1, 2, 3 } : null;
coinbase.TxInList[0].SigScript = new SignatureScript(height, padding);
ReadData("Enter previous block header: ", out byte[] prvBlkHdr);
var prvHdr = new BlockHeader();
b = prvHdr.TryDeserialize(new FastStreamReader(prvBlkHdr), out error);
Assert(b);
var block = new Block()
{
Header = new BlockHeader()
{
Version = prvHdr.Version,
PreviousBlockHeaderHash = prvHdr.GetHash(),
NBits = prvHdr.NBits,
BlockTime = prvHdr.BlockTime + 1,
},
};
ReadBool("Add invalid tx?(y/n)", out bool addTx);
Console.WriteLine();
block.TransactionList = addTx ? new ITransaction[] { coinbase, tx } : new ITransaction[] { coinbase };
coinbase.WitnessList = new Witness[1]
{
new Witness(new byte[][]{ new byte[32] })
};
byte[] root = block.ComputeWitnessMerkleRoot(new byte[32]);
coinbase.TxOutList[0].PubScript.SetToWitnessCommitment(root);
block.Header.MerkleRootHash = block.ComputeMerkleRoot();
var miner = new Miner();
miner.Mine(block, new System.Threading.CancellationToken(), 1).Wait();
Assert(b);
b = new BigInteger(block.Header.GetHash(), isUnsigned: true, isBigEndian: false) <= block.Header.NBits.ToBigInt();
Assert(b);
string final = block.Serialize().ToBase16();
Console.WriteLine($"Block ID: {block.GetBlockID()}");
Console.WriteLine("Raw block is:");
Console.WriteLine(final);
Console.WriteLine("-------------------------------------------");
Console.WriteLine("Press x to exit.");
ConsoleKeyInfo keyInfo = Console.ReadKey();
if (keyInfo.Key == ConsoleKey.X)
{
break;
}
}
}
private static void Assert(bool b)
{
if (!b)
{
Console.WriteLine("Something went wrong. Restart the app.");
}
}
private static void ReadBool(string message, out bool result)
{
while (true)
{
Console.Write(message);
ConsoleKeyInfo key = Console.ReadKey();
if (key.Key == ConsoleKey.Y)
{
result = true;
break;
}
else if (key.Key == ConsoleKey.N)
{
result = false;
break;
}
}
}
private static void ReadInt(string message, out int result)
{
while (true)
{
Console.Write(message);
string s = Console.ReadLine();
if (int.TryParse(s, out result))
{
break;
}
}
}
private static void ReadPrivateKey(string message, out PrivateKey result)
{
while (true)
{
Console.Write(message);
string s = Console.ReadLine();
try
{
result = new PrivateKey(s, NetworkType.RegTest);
break;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
private static void ReadData(string message, out byte[] result)
{
while (true)
{
Console.Write(message);
string s = Console.ReadLine();
try
{
result = Base16.Decode(s);
break;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment