任意性

ビットコインコアのバージョン0.10から、RedeemScript の内容は任意とすることができるようになった。それはビットコインのスクリプト言語を用いて、「所有権」が何を意味するかに関して自分の定義を作ることができるようになったということだ。

たとえば僕が、UTF8にバイト化された僕の誕生日を知っている人、もしくは 1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB に対応する秘密鍵を知っている人になら、誰でもビットコインをあげられるとしよう。

スクリプト言語の詳細についてはスコープ外だが、いろいろなウェブサイト上で簡単にドキュメントを見つけられるし、スタックベースの言語なのでアセンブラを触っていた人ならだれでも読むことができるはずだ。

注釈:(nopara73)僕は Davide De Rosa's tutorial が1番楽しいチュートリアルだと思う。

さあ最初は RedeemScript を作ってみよう。

注釈:このコードが正しく動くようにするために、プロジェクト -> 参照の編集 -> System.Numerics の順番にクリックして参照を設定しよう。

BitcoinAddress address = BitcoinAddress.Create("1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB");
var birth = Encoding.UTF8.GetBytes("18/07/1988");
var birthHash = Hashes.Hash256(birth);
Script redeemScript = new Script(
"OP_IF "
+ "OP_HASH256 " + Op.GetPushOp(birthHash.ToBytes()) + " OP_EQUAL " +
"OP_ELSE "
+ address.ScriptPubKey + " " +
"OP_ENDIF");

この RedeemScipt は、ScriptCoin を使う2つの方法を示している。2つの方法とは、1つが birthHash(僕の誕生日)を導出してくれるデータを知っていることで、もう1つがそのビットコインアドレスを持っていることだ。

たとえば、この redeemScript にビットコインを送ったとしよう。

var tx = new Transaction();
tx.Outputs.Add(new TxOut(Money.Parse("0.0001"), redeemScript.Hash));
ScriptCoin scriptCoin = tx.Outputs.AsCoins().First().ToScriptCoin(redeemScript);

そのアウトプットを使うトランザクションを作ってみよう。

//Create spending transaction
Transaction spending = new Transaction();
spending.AddInput(new TxIn(new OutPoint(tx, 0)));

最初の選択肢は僕の誕生日を知っていて、scriptSig の中でそれを証明することだ。

////Option 1 : Spender knows my birthdate
Op pushBirthdate = Op.GetPushOp(birth);
Op selectIf = OpcodeType.OP_1; //go to if
Op redeemBytes = Op.GetPushOp(redeemScript.ToBytes());
Script scriptSig = new Script(pushBirthdate, selectIf, redeemBytes);
spending.Inputs[0].ScriptSig = scriptSig;

scriptSig の中で、OP_1 を入れたから、条件判定の結果、RedeemScriptOP_IF の節に入ることがわかる。 このような scriptSig を生成するテンプレートはないが、手動でP2SHの scriptSig を作る方法がわかるだろう。

そして scriptSigscriptPubKey の所有権を証明することを確認できる。

//Verify the script pass
var result = spending
.Inputs
.AsIndexedInputs()
.First()
.VerifyScript(tx.Outputs[0].ScriptPubKey);
Console.WriteLine(result); // True

このビットコインを使用する2番目の方法は、1KF8kUVHK42XzgcmJF4Lxz4wcL5WDL97PB が自分のビットコインアドレスであることを証明することだ。

////Option 2 : Spender knows my private key
BitcoinSecret secret = new BitcoinSecret("...");
var sig = spending.SignInput(secret, scriptCoin);
var p2pkhProof = PayToPubkeyHashTemplate
.Instance
.GenerateScriptSig(sig, secret.PrivateKey.PubKey);
selectIf = OpcodeType.OP_0; //go to else
scriptSig = p2pkhProof + selectIf + redeemBytes;
spending.Inputs[0].ScriptSig = scriptSig;

以下でビットコインアドレスが自分のものであることの証明の結果を確認できる。

//Verify the script pass
result = spending
.Inputs
.AsIndexedInputs()
.First()
.VerifyScript(tx.Outputs[0].ScriptPubKey);
Console.WriteLine(result); // True
///////////