From 7251d8022118b8c5850f680a549682cf32dc75c7 Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Thu, 31 Aug 2017 19:25:28 +0100 Subject: [PATCH 1/8] WithdrawTransaction --- NBitcoin/SpvProof.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 NBitcoin/SpvProof.cs diff --git a/NBitcoin/SpvProof.cs b/NBitcoin/SpvProof.cs new file mode 100644 index 0000000000..7ccba73278 --- /dev/null +++ b/NBitcoin/SpvProof.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NBitcoin +{ + public class WithdrawTransaction :IBitcoinSerializable + { + public uint256 ParentGenesis; + + public SpvProof SpvProof; + + + public void ReadWrite(BitcoinStream stream) + { + throw new NotImplementedException(); + } + } + + public class SpvProof + { + private List Headers; + + } +} From 92969969eaee53542d19207c10a722ca1179d21f Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Fri, 1 Sep 2017 17:37:01 +0100 Subject: [PATCH 2/8] OP_WITHDRAWPROOFVERIFY --- NBitcoin/Script.cs | 5 +++-- NBitcoin/ScriptEvaluationContext.cs | 21 ++++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/NBitcoin/Script.cs b/NBitcoin/Script.cs index 0b6b6cf7d8..0b0d2a3e5d 100644 --- a/NBitcoin/Script.cs +++ b/NBitcoin/Script.cs @@ -1,10 +1,10 @@ -using System.Runtime.InteropServices; -using NBitcoin.Crypto; +using NBitcoin.Crypto; using NBitcoin.DataEncoders; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; namespace NBitcoin @@ -308,6 +308,7 @@ public enum OpcodeType : byte OP_NOP2 = 0xb1, OP_NOP3 = 0xb2, OP_NOP4 = 0xb3, + OP_WITHDRAWPROOFVERIFY = OP_NOP4, OP_NOP5 = 0xb4, OP_NOP6 = 0xb5, OP_NOP7 = 0xb6, diff --git a/NBitcoin/ScriptEvaluationContext.cs b/NBitcoin/ScriptEvaluationContext.cs index 8ce102bceb..279241c8e3 100644 --- a/NBitcoin/ScriptEvaluationContext.cs +++ b/NBitcoin/ScriptEvaluationContext.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using NBitcoin.Crypto; +using NBitcoin.Crypto; using System; using System.Collections; using System.Collections.Generic; @@ -823,7 +822,7 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) } - case OpcodeType.OP_NOP4: + //case OpcodeType.OP_NOP4: case OpcodeType.OP_NOP5: case OpcodeType.OP_NOP6: case OpcodeType.OP_NOP7: @@ -1463,6 +1462,22 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) } break; } + + case OpcodeType.OP_WITHDRAWPROOFVERIFY: + + { + + // There are two cases + // 1. a locking output that will verify the output amount is correct + // 2. a spending output that will unlock some coins + + + if (_stack.Count != 2 ) + return SetError(ScriptError.InvalidStackOperation); + + break; + } + default: return SetError(ScriptError.BadOpCode); } From 5ea6cd385d4269440262446e6a419984c35bbe45 Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Sat, 2 Sep 2017 10:26:11 +0100 Subject: [PATCH 3/8] Adding some properties --- NBitcoin/SpvProof.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NBitcoin/SpvProof.cs b/NBitcoin/SpvProof.cs index 7ccba73278..6fc5cf3c3f 100644 --- a/NBitcoin/SpvProof.cs +++ b/NBitcoin/SpvProof.cs @@ -19,7 +19,12 @@ public void ReadWrite(BitcoinStream stream) public class SpvProof { - private List Headers; + public List Headers; + public uint256 ParentGenesis; + + public Transaction Lock; + + public MerkleBlock } } From 0e87f8954bc6147a54f3b825f21245369b73f311 Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Mon, 4 Sep 2017 18:52:58 +0100 Subject: [PATCH 4/8] Create the proof --- NBitcoin/SpvProof.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/NBitcoin/SpvProof.cs b/NBitcoin/SpvProof.cs index 6fc5cf3c3f..d47192ff2c 100644 --- a/NBitcoin/SpvProof.cs +++ b/NBitcoin/SpvProof.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace NBitcoin { @@ -19,12 +18,16 @@ public void ReadWrite(BitcoinStream stream) public class SpvProof { - public List Headers; + public List SpvHeaders; - public uint256 ParentGenesis; + public uint256 Genesis; public Transaction Lock; - public MerkleBlock + public Transaction CoinBase; + + public Transaction CoinStake; + + public PartialMerkleTree MerkleProof; } } From ab202c644a0e3317afe6d08e5fcf3a1ab2ae5d71 Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Wed, 6 Sep 2017 18:43:53 +0100 Subject: [PATCH 5/8] Adding code to push an spv proof to the script stack --- NBitcoin.Tests/sidechains_tests.cs | 107 ++++++++++++++++++++++++++++ NBitcoin/PartialMerkleTree.cs | 8 +++ NBitcoin/ScriptEvaluationContext.cs | 20 +++++- NBitcoin/SpvProof.cs | 55 ++++++++++++-- 4 files changed, 181 insertions(+), 9 deletions(-) create mode 100644 NBitcoin.Tests/sidechains_tests.cs diff --git a/NBitcoin.Tests/sidechains_tests.cs b/NBitcoin.Tests/sidechains_tests.cs new file mode 100644 index 0000000000..82ba0e8362 --- /dev/null +++ b/NBitcoin.Tests/sidechains_tests.cs @@ -0,0 +1,107 @@ +using NBitcoin.DataEncoders; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace NBitcoin.Tests +{ + public class sidechains + { + + [Fact] + [Trait("UnitTest", "UnitTest")] + public static void CreateWithdrawScript() + { + // build a sidechain genesis with a locked output of 1000 coins + + Transaction sidechainGenesis = new Transaction(); + sidechainGenesis.Version = 1; + sidechainGenesis.Time = (uint) DateTime.UtcNow.ToUnixTimestamp(); + sidechainGenesis.AddInput(new TxIn + { + ScriptSig = new Script(Op.GetPushOp(Encoders.ASCII.DecodeData("Testing a sidechain"))) + }); + sidechainGenesis.AddOutput(new TxOut() + { + Value = Money.Coins(1000), + ScriptPubKey = new Script(new Op + { + Code = OpcodeType.OP_WITHDRAWPROOFVERIFY, + //PushData = new[] {(byte) 42} + + }) + }); + + // build a withdraw lock transaction from parent chain that spends 500 coins + + var key = new Key(); + var network = Network.RegTest; + + // a block that has an output. + var block = CreateBlockWithCoinbase(network, network.GetGenesis(), key, 1); + var lockTrx = new Transaction(); + lockTrx.AddInput(new TxIn(new OutPoint(uint256.Zero, 0))); // a fake input that spends 250 + lockTrx.AddInput(new TxIn(new OutPoint(uint256.Zero, 0))); // a fake input that spends 250 + lockTrx.AddOutput(new TxOut(Money.Coins(500), + new Script(new Op {Code = OpcodeType.OP_WITHDRAWPROOFVERIFY}))); // lock the output + // TODO: how do we represent the target on the sidechain? + block.AddTransaction(lockTrx); + block.UpdateMerkleRoot(); + + // mine two more blocks + var block1 = CreateBlockWithCoinbase(network, block, key, 2); + var block2 = CreateBlockWithCoinbase(network, block1, key, 3); + + // Create an SPV proof, a transaction that can withdraw to the sidechain + var proof = new SpvProof(); + proof.CoinBase = block.Transactions.First(); + proof.Lock = lockTrx; + var merkleBlock = new MerkleBlock(block, new[] {lockTrx.GetHash()}); + proof.MerkleProof = merkleBlock.PartialMerkleTree; + proof.SpvHeaders = new SpvHeaders {Headers = new List {block.Header, block1.Header, block2.Header}}; + proof.Genesis = network.GenesisHash; + + // verify the transaction script + + var scriptSignature = new Script( + Op.GetPushOp(proof.Genesis.ToBytes()), + Op.GetPushOp(proof.CoinBase.ToBytes()), + Op.GetPushOp(proof.Lock.ToBytes()), + Op.GetPushOp(proof.MerkleProof.ToBytes()), + Op.GetPushOp(proof.SpvHeaders.ToBytes())); + + var withdrawTrx = new Transaction(); + withdrawTrx.AddInput(new TxIn(new OutPoint(sidechainGenesis, 0), scriptSignature)); + withdrawTrx.AddOutput(new TxOut(500, key.ScriptPubKey)); + withdrawTrx.AddOutput(new TxOut(500, new Script(new Op {Code = OpcodeType.OP_WITHDRAWPROOFVERIFY}))); + + var scriptSig = withdrawTrx.Inputs.First().ScriptSig; + var output = sidechainGenesis.Outputs.First(); + var scriptPubKey = sidechainGenesis.Outputs.First().ScriptPubKey; + + + var result = Script.VerifyScript(scriptSig, scriptPubKey, withdrawTrx, 0); + + } + + + private static Block CreateBlockWithCoinbase(Network network, Block previous, Key key, int index) + { + Block block = new Block(); + block.Header.HashPrevBlock = previous.GetHash(); + var tip = new ChainedBlock(previous.Header, index); + block.Header.Bits = block.Header.GetWorkRequired(network, tip); + block.Header.UpdateTime(network, tip); + + var coinbase = new Transaction(); + coinbase.AddInput(TxIn.CreateCoinbase(tip.Height + 1)); + coinbase.AddOutput(new TxOut(network.GetReward(tip.Height + 1), key)); + block.AddTransaction(coinbase); + + block.UpdateMerkleRoot(); + + return block; + } + } +} diff --git a/NBitcoin/PartialMerkleTree.cs b/NBitcoin/PartialMerkleTree.cs index ac1290b880..0e44086e74 100644 --- a/NBitcoin/PartialMerkleTree.cs +++ b/NBitcoin/PartialMerkleTree.cs @@ -11,6 +11,14 @@ public PartialMerkleTree() { } + + public PartialMerkleTree(byte[] bytes) + : this() + { + this.FromBytes(bytes); + } + + uint _TransactionCount; public uint TransactionCount { diff --git a/NBitcoin/ScriptEvaluationContext.cs b/NBitcoin/ScriptEvaluationContext.cs index 279241c8e3..edcb0ce53d 100644 --- a/NBitcoin/ScriptEvaluationContext.cs +++ b/NBitcoin/ScriptEvaluationContext.cs @@ -1472,10 +1472,26 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) // 2. a spending output that will unlock some coins - if (_stack.Count != 2 ) + if (_stack.Count != 5) return SetError(ScriptError.InvalidStackOperation); - break; + + var arrayList = new List + { + _stack.Pop(), + _stack.Pop(), + _stack.Pop(), + _stack.Pop(), + _stack.Pop(), + }; + + arrayList.Reverse(); + var proof = SpvProof.CreateProof(arrayList); + + // validate all items. + + + break; } default: diff --git a/NBitcoin/SpvProof.cs b/NBitcoin/SpvProof.cs index d47192ff2c..171122244b 100644 --- a/NBitcoin/SpvProof.cs +++ b/NBitcoin/SpvProof.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; namespace NBitcoin { @@ -17,17 +18,57 @@ public void ReadWrite(BitcoinStream stream) } public class SpvProof - { - public List SpvHeaders; + { + public uint256 Genesis; - public uint256 Genesis; + public SpvHeaders SpvHeaders; - public Transaction Lock; + public Transaction Lock; public Transaction CoinBase; - public Transaction CoinStake; - public PartialMerkleTree MerkleProof; - } + + public static Script CreateScript(SpvProof proof) + { + var scriptSignature = new Script( + Op.GetPushOp(proof.Genesis.ToBytes()), + Op.GetPushOp(proof.CoinBase.ToBytes()), + Op.GetPushOp(proof.Lock.ToBytes()), + Op.GetPushOp(proof.MerkleProof.ToBytes()), + Op.GetPushOp(proof.SpvHeaders.ToBytes())); + + return scriptSignature; + } + + public static SpvProof CreateProof(IEnumerable stack) + { + var items = stack.ToArray(); + var proof = new SpvProof(); + proof.Genesis = new uint256(items[0]); + proof.CoinBase = new Transaction(items[1]); + proof.Lock = new Transaction(items[2]); + proof.MerkleProof = new PartialMerkleTree(items[3]); + proof.SpvHeaders = new SpvHeaders(items[4]); + return proof; + } + } + + public class SpvHeaders : IBitcoinSerializable + { + public List Headers; + + public SpvHeaders() + { } + + public SpvHeaders(byte[] bytes) + { + this.FromBytes(bytes); + } + + public void ReadWrite(BitcoinStream stream) + { + stream.ReadWrite(ref this.Headers); + } + } } From 93ab3bc90420368eae9a957640b34d1ef7834225 Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Fri, 8 Sep 2017 17:58:27 +0100 Subject: [PATCH 6/8] Create a sidechain withdraw --- NBitcoin.Tests/sidechains_tests.cs | 14 ++---- NBitcoin/ScriptEvaluationContext.cs | 77 +++++++++++++++++++++++++---- NBitcoin/SpvProof.cs | 58 ++++++++++++++-------- 3 files changed, 110 insertions(+), 39 deletions(-) diff --git a/NBitcoin.Tests/sidechains_tests.cs b/NBitcoin.Tests/sidechains_tests.cs index 82ba0e8362..ad33a895c6 100644 --- a/NBitcoin.Tests/sidechains_tests.cs +++ b/NBitcoin.Tests/sidechains_tests.cs @@ -57,19 +57,15 @@ public static void CreateWithdrawScript() var proof = new SpvProof(); proof.CoinBase = block.Transactions.First(); proof.Lock = lockTrx; + proof.OutputIndex = 0; var merkleBlock = new MerkleBlock(block, new[] {lockTrx.GetHash()}); proof.MerkleProof = merkleBlock.PartialMerkleTree; proof.SpvHeaders = new SpvHeaders {Headers = new List {block.Header, block1.Header, block2.Header}}; proof.Genesis = network.GenesisHash; - + proof.DestinationScript = key.ScriptPubKey; // verify the transaction script - var scriptSignature = new Script( - Op.GetPushOp(proof.Genesis.ToBytes()), - Op.GetPushOp(proof.CoinBase.ToBytes()), - Op.GetPushOp(proof.Lock.ToBytes()), - Op.GetPushOp(proof.MerkleProof.ToBytes()), - Op.GetPushOp(proof.SpvHeaders.ToBytes())); + var scriptSignature = SpvProof.CreateScript(proof); var withdrawTrx = new Transaction(); withdrawTrx.AddInput(new TxIn(new OutPoint(sidechainGenesis, 0), scriptSignature)); @@ -81,8 +77,8 @@ public static void CreateWithdrawScript() var scriptPubKey = sidechainGenesis.Outputs.First().ScriptPubKey; - var result = Script.VerifyScript(scriptSig, scriptPubKey, withdrawTrx, 0); - + var result = Script.VerifyScript(scriptSig, scriptPubKey, withdrawTrx, 0, output.Value); + Assert.True(result); } diff --git a/NBitcoin/ScriptEvaluationContext.cs b/NBitcoin/ScriptEvaluationContext.cs index edcb0ce53d..b20cfb8610 100644 --- a/NBitcoin/ScriptEvaluationContext.cs +++ b/NBitcoin/ScriptEvaluationContext.cs @@ -61,6 +61,19 @@ public enum ScriptError NullFail, MinimalIf, WitnessPubkeyType, + + /* sidechains */ + WithdrawVerifyFormat, + WithdrawVerifyBlock, + WithdrawVerifyLockTx, + WithdrawVerifyOutput, + WithdrawVerifyOutputScriptdest, + WithdrawVerifyRelockScriptval, + WithdrawVerifyOutputval, + WithdrawVerifyOutputscript, + + WithdrawVerifyBlockUnconfirmed, + WithdrawVerifyBlindedAmounts, } public class TransactionChecker @@ -1466,30 +1479,74 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) case OpcodeType.OP_WITHDRAWPROOFVERIFY: { - // There are two cases // 1. a locking output that will verify the output amount is correct // 2. a spending output that will unlock some coins - - if (_stack.Count != 5) + if (_stack.Count != 7) return SetError(ScriptError.InvalidStackOperation); - var arrayList = new List { - _stack.Pop(), - _stack.Pop(), - _stack.Pop(), - _stack.Pop(), - _stack.Pop(), + _stack.Pop(), // SpvHeaders + _stack.Pop(), // MerkleProof + _stack.Pop(), // OutputIndex + _stack.Pop(), // Lock + _stack.Pop(), // CoinBase + _stack.Pop(), // Genesis + _stack.Pop(), // P2SH target }; arrayList.Reverse(); var proof = SpvProof.CreateProof(arrayList); - // validate all items. + // validate the target is one of the outputs. + // currently only allow two outputs: + // - the spending of the lock + // - the re-lock of the result + + if(checker.Transaction.Outputs.Count != 2) + return SetError(ScriptError.WithdrawVerifyOutput); + + var spender = checker.Transaction.Outputs.Where(o => o.ScriptPubKey == proof.DestinationScript); + if (spender.Count() != 1) + return SetError(ScriptError.WithdrawVerifyOutputscript); + + // check that the output value is within the unlocked coins + + var withdrawScript = new Script(new[] {new Op {Code = OpcodeType.OP_WITHDRAWPROOFVERIFY}}); + var relocks = checker.Transaction.Outputs.Where(o => o.ScriptPubKey == withdrawScript); + + if (relocks.Count() != 1) + return SetError(ScriptError.WithdrawVerifyOutput); + + if (relocks.First().Value > checker.Amount) + return SetError(ScriptError.WithdrawVerifyOutputval); + + // check that the Lock transaction is in the first header + var first = proof.SpvHeaders.Headers.First(); + if (!proof.MerkleProof.Check(first.HashMerkleRoot)) + return SetError(ScriptError.WithdrawVerifyLockTx); + + // check that the coinbase is in the first header + + if (!proof.MerkleProof.Check(first.HashMerkleRoot)) + return SetError(ScriptError.WithdrawVerifyLockTx); + + // verify the amount of work done + proof.SpvHeaders.Headers.Reverse(); + var current = proof.SpvHeaders.Headers.First(); + + foreach (var header in proof.SpvHeaders.Headers.Skip(1)) + { + if(current.HashPrevBlock != header.GetHash()) + return SetError(ScriptError.WithdrawVerifyBlock); + current = header; + + // TODO: check the work + } + _stack.Push(vchTrue); break; } diff --git a/NBitcoin/SpvProof.cs b/NBitcoin/SpvProof.cs index 171122244b..a006d01cbe 100644 --- a/NBitcoin/SpvProof.cs +++ b/NBitcoin/SpvProof.cs @@ -1,23 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.IO; using System.Linq; namespace NBitcoin { - public class WithdrawTransaction :IBitcoinSerializable - { - public uint256 ParentGenesis; - - public SpvProof SpvProof; - - - public void ReadWrite(BitcoinStream stream) - { - throw new NotImplementedException(); - } - } - - public class SpvProof + public class SpvProof { public uint256 Genesis; @@ -25,18 +12,24 @@ public class SpvProof public Transaction Lock; + public int OutputIndex; + public Transaction CoinBase; public PartialMerkleTree MerkleProof; + public Script DestinationScript; + public static Script CreateScript(SpvProof proof) { var scriptSignature = new Script( Op.GetPushOp(proof.Genesis.ToBytes()), Op.GetPushOp(proof.CoinBase.ToBytes()), + Op.GetPushOp(WriteIndex(proof.OutputIndex)), Op.GetPushOp(proof.Lock.ToBytes()), Op.GetPushOp(proof.MerkleProof.ToBytes()), - Op.GetPushOp(proof.SpvHeaders.ToBytes())); + Op.GetPushOp(proof.SpvHeaders.ToBytes()), + Op.GetPushOp(proof.DestinationScript.ToBytes())); return scriptSignature; } @@ -47,11 +40,34 @@ public static SpvProof CreateProof(IEnumerable stack) var proof = new SpvProof(); proof.Genesis = new uint256(items[0]); proof.CoinBase = new Transaction(items[1]); - proof.Lock = new Transaction(items[2]); - proof.MerkleProof = new PartialMerkleTree(items[3]); - proof.SpvHeaders = new SpvHeaders(items[4]); + proof.OutputIndex = ReadIndex(items[2]); + proof.Lock = new Transaction(items[3]); + proof.MerkleProof = new PartialMerkleTree(items[4]); + proof.SpvHeaders = new SpvHeaders(items[5]); + proof.DestinationScript = new Script(items[6]); return proof; } + + public static int ReadIndex(byte[] array) + { + using (var mem = new MemoryStream(array)) + { + int ret = 0; + var stream = new BitcoinStream(mem, false); + stream.ReadWrite(ref ret); + return ret; + } + } + + public static byte[] WriteIndex(int number) + { + using (var mem = new MemoryStream()) + { + var stream = new BitcoinStream(mem, true); + stream.ReadWrite(ref number); + return mem.ToArray(); + } + } } public class SpvHeaders : IBitcoinSerializable @@ -71,4 +87,6 @@ public void ReadWrite(BitcoinStream stream) stream.ReadWrite(ref this.Headers); } } + + } From 3467db671638b9d54fa8d69528e7a304407c0d9a Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Fri, 8 Sep 2017 18:09:05 +0100 Subject: [PATCH 7/8] Add some comments --- NBitcoin/ScriptEvaluationContext.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/NBitcoin/ScriptEvaluationContext.cs b/NBitcoin/ScriptEvaluationContext.cs index b20cfb8610..8847b7e2b2 100644 --- a/NBitcoin/ScriptEvaluationContext.cs +++ b/NBitcoin/ScriptEvaluationContext.cs @@ -1479,9 +1479,14 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) case OpcodeType.OP_WITHDRAWPROOFVERIFY: { - // There are two cases - // 1. a locking output that will verify the output amount is correct - // 2. a spending output that will unlock some coins + // This op code expects the following on the stack + // 1. a list of spv headers + // 2. a MerkleProof of the locking trx on the parent chain + // 3. the OutputIndex of the locking trx + // 4. the locking trx itself + // 5. the CoinBase of the block the lock was confirmed in + // 6. Genesis of parent + // 7. ScriptPubKey of the destination script if (_stack.Count != 7) return SetError(ScriptError.InvalidStackOperation); From 03f7f19fb21eb77d3e53d86d0930b322a9667196 Mon Sep 17 00:00:00 2001 From: Dan Gershony Date: Mon, 11 Sep 2017 14:30:14 +0100 Subject: [PATCH 8/8] Adding some comments --- NBitcoin.Tests/sidechains_tests.cs | 2 -- NBitcoin/ScriptEvaluationContext.cs | 4 +-- NBitcoin/SpvProof.cs | 39 +++++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/NBitcoin.Tests/sidechains_tests.cs b/NBitcoin.Tests/sidechains_tests.cs index ad33a895c6..35d9ce4e4f 100644 --- a/NBitcoin.Tests/sidechains_tests.cs +++ b/NBitcoin.Tests/sidechains_tests.cs @@ -28,8 +28,6 @@ public static void CreateWithdrawScript() ScriptPubKey = new Script(new Op { Code = OpcodeType.OP_WITHDRAWPROOFVERIFY, - //PushData = new[] {(byte) 42} - }) }); diff --git a/NBitcoin/ScriptEvaluationContext.cs b/NBitcoin/ScriptEvaluationContext.cs index 8847b7e2b2..636c47d30c 100644 --- a/NBitcoin/ScriptEvaluationContext.cs +++ b/NBitcoin/ScriptEvaluationContext.cs @@ -1499,7 +1499,7 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) _stack.Pop(), // Lock _stack.Pop(), // CoinBase _stack.Pop(), // Genesis - _stack.Pop(), // P2SH target + _stack.Pop(), // SciptPubkey target }; arrayList.Reverse(); @@ -1518,7 +1518,6 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) return SetError(ScriptError.WithdrawVerifyOutputscript); // check that the output value is within the unlocked coins - var withdrawScript = new Script(new[] {new Op {Code = OpcodeType.OP_WITHDRAWPROOFVERIFY}}); var relocks = checker.Transaction.Outputs.Where(o => o.ScriptPubKey == withdrawScript); @@ -1534,7 +1533,6 @@ bool EvalScript(Script s, TransactionChecker checker, int hashversion) return SetError(ScriptError.WithdrawVerifyLockTx); // check that the coinbase is in the first header - if (!proof.MerkleProof.Check(first.HashMerkleRoot)) return SetError(ScriptError.WithdrawVerifyLockTx); diff --git a/NBitcoin/SpvProof.cs b/NBitcoin/SpvProof.cs index a006d01cbe..a1972954e6 100644 --- a/NBitcoin/SpvProof.cs +++ b/NBitcoin/SpvProof.cs @@ -4,20 +4,52 @@ namespace NBitcoin { + /// + /// An SpvProof (Simplified Payment Verification) that is used to proof an output is locked (undependable). + /// public class SpvProof { + /// + /// The genesis hash of the chain where the withdraw lock was created. + /// public uint256 Genesis; + /// + /// A list of headers on the chain that confirmed the withdraw. + /// + /// + /// The first header represents the block that contains the withdraw lock. + /// The rest of the headers represent work (how many blocks the withdraw is berried under). + /// The headers are expected to be a chained of block headers. + /// public SpvHeaders SpvHeaders; + /// + /// A transaction that locked some coins to a special op code . + /// public Transaction Lock; + /// + /// The index of the output in the that lock transaction that is being refereed to by this SPV Proof. + /// public int OutputIndex; + /// + /// The coinbase of the block that confirmed the locked transaction. + /// + /// + /// The coinbase can be used to know the block height of the locked transaction. + /// public Transaction CoinBase; + /// + /// A proof that verifies the locking transaction was included in a block. + /// public PartialMerkleTree MerkleProof; + /// + /// The recipient of the locking transaction. + /// public Script DestinationScript; public static Script CreateScript(SpvProof proof) @@ -48,7 +80,7 @@ public static SpvProof CreateProof(IEnumerable stack) return proof; } - public static int ReadIndex(byte[] array) + private static int ReadIndex(byte[] array) { using (var mem = new MemoryStream(array)) { @@ -59,7 +91,7 @@ public static int ReadIndex(byte[] array) } } - public static byte[] WriteIndex(int number) + private static byte[] WriteIndex(int number) { using (var mem = new MemoryStream()) { @@ -70,6 +102,9 @@ public static byte[] WriteIndex(int number) } } + /// + /// A class to serialize a list of block headers. + /// public class SpvHeaders : IBitcoinSerializable { public List Headers;