Arbitrary
From Bitcoin 0.10, the RedeemScript can be arbitrary, which means that with the script language of Bitcoin, you can create your own definition of what “ownership” means.
For example, I can give money to whoever knows either my date of birth (dd/mm/yyyy) serialized in UTF-8 or the private key of 1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB.
The details of the script language are out of scope. You can easily find the documentation on various websites. The Bitcoin script language is a stack based language so everyone having done some assembler should be able to read it.
Note: (nopara73) I find Davide De Rosa's tutorial as the most enjoyable one.
So first, let’s build the RedeemScript,
Note: For this code to work right click References -> Add Reference... -> Find System.Numerics
1
BitcoinAddress address = BitcoinAddress.Create("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB", Network.Main);
2
var birth = Encoding.UTF8.GetBytes("18/07/1988");
3
var birthHash = Hashes.DoubleSHA256(birth);
4
Script redeemScript = new Script(
5
"OP_IF "
6
+ "OP_HASH256 " + Op.GetPushOp(birthHash.ToBytes()) + " OP_EQUAL " +
7
"OP_ELSE "
8
+ address.ScriptPubKey + " " +
9
"OP_ENDIF");
Copied!
This RedeemScript means that there are 2 ways of spending such ScriptCoin: Either you know the data that gives birthHash (my birthdate) or you own the bitcoin address.
Let’s say I sent money to such redeemScript:
1
var tx = Network.Main.CreateTransaction();
2
tx.Outputs.Add(Money.Parse("0.0001"), redeemScript.Hash);
3
ScriptCoin scriptCoin = tx.Outputs.AsCoins().First().ToScriptCoin(redeemScript);
Copied!
So let’s create a transaction that wants to spend such output:
1
//Create spending transaction
2
Transaction spending = Network.Main.CreateTransaction();
3
spending.AddInput(new TxIn(new OutPoint(tx, 0)));
Copied!
The first option is to know my birth date and to prove it in the scriptSig:
1
////Option 1 : Spender knows my birthdate
2
Op pushBirthdate = Op.GetPushOp(birth);
3
Op selectIf = OpcodeType.OP_1; //go to if
4
Op redeemBytes = Op.GetPushOp(redeemScript.ToBytes());
5
Script scriptSig = new Script(pushBirthdate, selectIf, redeemBytes);
6
spending.Inputs[0].ScriptSig = scriptSig;
Copied!
You can see that in the scriptSig I push OP_1 so I enter in the OP_IF of my RedeemScript. Since there is no backed-in template, for creating such scriptSig, you can see how to build a P2SH scriptSig by hand.
Then you can check that the scriptSig proves the ownership of the scriptPubKey:
1
//Verify the script pass
2
var result = spending
3
.Inputs
4
.AsIndexedInputs()
5
.First()
6
.VerifyScript(tx.Outputs[0].ScriptPubKey);
7
Console.WriteLine(result); // True
Copied!
The second way of spending the coin is by proving ownership of 1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB.
1
////Option 2 : Spender knows my private key
2
BitcoinSecret secret = new BitcoinSecret("...", Network.Mainnet);
3
var sig = spending.SignInput(secret, scriptCoin);
4
var p2pkhProof = PayToPubkeyHashTemplate
5
.Instance
6
.GenerateScriptSig(sig, secret.PrivateKey.PubKey);
7
selectIf = OpcodeType.OP_0; //go to else
8
scriptSig = p2pkhProof + selectIf + redeemBytes;
9
spending.Inputs[0].ScriptSig = scriptSig;
Copied!
And ownership is also proven:
1
//Verify the script pass
2
result = spending
3
.Inputs
4
.AsIndexedInputs()
5
.First()
6
.VerifyScript(tx.Outputs[0].ScriptPubKey);
7
Console.WriteLine(result); // True
8
///////////
Copied!
Last modified 1yr ago
Copy link