Created
February 23, 2021 08:18
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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