forked from MetacoSA/NBitcoin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ForkIdTransaction.cs
227 lines (197 loc) · 6.41 KB
/
ForkIdTransaction.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using NBitcoin.Crypto;
namespace NBitcoin.Altcoins
{
public class ForkIdTransaction : Transaction, IHasForkId
{
public ForkIdTransaction(uint forkId, bool supportSegwit, ConsensusFactory consensusFactory)
{
_ForkId = forkId;
_SupportSegwit = supportSegwit;
_Factory = consensusFactory;
}
ConsensusFactory _Factory;
public override ConsensusFactory GetConsensusFactory()
{
return _Factory;
}
private readonly bool _SupportSegwit;
public bool SupportSegwit
{
get
{
return _SupportSegwit;
}
}
private readonly uint _ForkId;
public uint ForkId
{
get
{
return _ForkId;
}
}
public override uint256 GetSignatureHash(Script scriptCode, int nIn, SigHash nHashType, TxOut spentOutput, HashVersion sigversion, PrecomputedTransactionData precomputedTransactionData)
{
uint nForkHashType = (uint)nHashType;
if(UsesForkId(nHashType))
nForkHashType |= ForkId << 8;
if((SupportSegwit && sigversion == HashVersion.WitnessV0) || UsesForkId(nHashType))
{
if (spentOutput?.Value == null || spentOutput.Value == TxOut.NullMoney)
throw new ArgumentException("The output being signed with the amount must be provided", nameof(spentOutput));
uint256 hashPrevouts = uint256.Zero;
uint256 hashSequence = uint256.Zero;
uint256 hashOutputs = uint256.Zero;
if((nHashType & SigHash.AnyoneCanPay) == 0)
{
hashPrevouts = precomputedTransactionData == null ?
GetHashPrevouts() : precomputedTransactionData.HashPrevouts;
}
if((nHashType & SigHash.AnyoneCanPay) == 0 && ((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
{
hashSequence = precomputedTransactionData == null ?
GetHashSequence() : precomputedTransactionData.HashSequence;
}
if(((uint)nHashType & 0x1f) != (uint)SigHash.Single && ((uint)nHashType & 0x1f) != (uint)SigHash.None)
{
hashOutputs = precomputedTransactionData == null ?
GetHashOutputs() : precomputedTransactionData.HashOutputs;
}
else if(((uint)nHashType & 0x1f) == (uint)SigHash.Single && nIn < this.Outputs.Count)
{
BitcoinStream ss = CreateHashWriter(sigversion);
ss.ReadWrite(this.Outputs[nIn]);
hashOutputs = GetHash(ss);
}
BitcoinStream sss = CreateHashWriter(sigversion);
// Version
sss.ReadWrite(this.Version);
// Input prevouts/nSequence (none/all, depending on flags)
sss.ReadWrite(hashPrevouts);
sss.ReadWrite(hashSequence);
// The input being signed (replacing the scriptSig with scriptCode + amount)
// The prevout may already be contained in hashPrevout, and the nSequence
// may already be contain in hashSequence.
sss.ReadWrite(Inputs[nIn].PrevOut);
sss.ReadWrite(scriptCode);
sss.ReadWrite(spentOutput.Value.Satoshi);
sss.ReadWrite(Inputs[nIn].Sequence);
// Outputs (none/one/all, depending on flags)
sss.ReadWrite(hashOutputs);
// Locktime
sss.ReadWriteStruct(LockTime);
// Sighash type
sss.ReadWrite(nForkHashType);
return GetHash(sss);
}
if(nIn >= Inputs.Count)
{
return uint256.One;
}
var hashType = nHashType & (SigHash)31;
// Check for invalid use of SIGHASH_SINGLE
if(hashType == SigHash.Single)
{
if(nIn >= Outputs.Count)
{
return uint256.One;
}
}
var scriptCopy = scriptCode.Clone();
scriptCode = scriptCopy.FindAndDelete(OpcodeType.OP_CODESEPARATOR);
var txCopy = GetConsensusFactory().CreateTransaction();
txCopy.FromBytes(this.ToBytes());
//Set all TxIn script to empty string
foreach(var txin in txCopy.Inputs)
{
txin.ScriptSig = new Script();
}
//Copy subscript into the txin script you are checking
txCopy.Inputs[nIn].ScriptSig = scriptCopy;
if(hashType == SigHash.None)
{
//The output of txCopy is set to a vector of zero size.
txCopy.Outputs.Clear();
//All other inputs aside from the current input in txCopy have their nSequence index set to zero
foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn))
input.Sequence = 0;
}
else if(hashType == SigHash.Single)
{
//The output of txCopy is resized to the size of the current input index+1.
txCopy.Outputs.RemoveRange(nIn + 1, txCopy.Outputs.Count - (nIn + 1));
//All other txCopy outputs aside from the output that is the same as the current input index are set to a blank script and a value of (long) -1.
for(var i = 0; i < txCopy.Outputs.Count; i++)
{
if(i == nIn)
continue;
txCopy.Outputs[i] = new TxOut();
}
//All other txCopy inputs aside from the current input are set to have an nSequence index of zero.
foreach(var input in txCopy.Inputs.Where((x, i) => i != nIn))
input.Sequence = 0;
}
if((nHashType & SigHash.AnyoneCanPay) != 0)
{
//The txCopy input vector is resized to a length of one.
var script = txCopy.Inputs[nIn];
txCopy.Inputs.Clear();
txCopy.Inputs.Add(script);
//The subScript (lead in by its length as a var-integer encoded!) is set as the first and only member of this vector.
txCopy.Inputs[0].ScriptSig = scriptCopy;
}
//Serialize TxCopy, append 4 byte hashtypecode
var stream = CreateHashWriter(sigversion);
txCopy.ReadWrite(stream);
stream.ReadWrite(nForkHashType);
return GetHash(stream);
}
private bool UsesForkId(SigHash nHashType)
{
return ((uint)nHashType & 0x40u) != 0;
}
private static uint256 GetHash(BitcoinStream stream)
{
var preimage = ((HashStream)stream.Inner).GetHash();
stream.Inner.Dispose();
return preimage;
}
internal override uint256 GetHashOutputs()
{
uint256 hashOutputs;
BitcoinStream ss = CreateHashWriter(HashVersion.WitnessV0);
foreach(var txout in Outputs)
{
ss.ReadWrite(txout);
}
hashOutputs = GetHash(ss);
return hashOutputs;
}
internal override uint256 GetHashSequence()
{
uint256 hashSequence;
BitcoinStream ss = CreateHashWriter(HashVersion.WitnessV0);
foreach(var input in Inputs)
{
ss.ReadWrite(input.Sequence);
}
hashSequence = GetHash(ss);
return hashSequence;
}
internal override uint256 GetHashPrevouts()
{
uint256 hashPrevouts;
BitcoinStream ss = CreateHashWriter(HashVersion.WitnessV0);
foreach(var input in Inputs)
{
ss.ReadWrite(input.PrevOut);
}
hashPrevouts = GetHash(ss);
return hashPrevouts;
}
}
}