Skip to content

Commit 24d59cc

Browse files
committed
More refactor
1 parent 26ed39f commit 24d59cc

File tree

8 files changed

+246
-289
lines changed

8 files changed

+246
-289
lines changed

NBitcoin/BIP174/Maps.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public void Add<T>(byte[] key, T val)
105105
}
106106
}
107107

108+
public IEnumerable<KeyValuePair<byte[], T>> RemoveAll<T>(byte prefixKey) => RemoveAll<T>([prefixKey]);
108109
public IEnumerable<KeyValuePair<byte[],T>> RemoveAll<T>(byte[] prefixKey)
109110
{
110111
var keys = this.Keys.Where(k => StartWith(prefixKey, k)).ToList();
@@ -127,7 +128,7 @@ private static bool StartWith(byte[] prefix, byte[] data)
127128
}
128129
return true;
129130
}
130-
131+
public bool TryRemove<T>(byte key, [MaybeNullWhen(false)] out T value) => TryRemove<T>([key], out value);
131132
public bool TryRemove<T>(byte[] key, [MaybeNullWhen(false)] out T value)
132133
{
133134
value = default;

NBitcoin/BIP174/PSBT0.cs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ internal PSBT0(Maps maps, Network network) : base(maps, network, PSBTVersion.PSB
102102
var map = maps[index];
103103
if (map.Keys.Any(bytes => bytes.Length == 1 && PSBT2Constants.PSBT_V0_OUTPUT_EXCLUSIONSET.Contains(bytes[0])))
104104
throw new FormatException("Invalid PSBT v0. Contains v2 fields");
105-
Outputs.Add(new PSBTOutput(map, this, indexedOutput.N, indexedOutput.TxOut));
105+
Outputs.Add(new PSBT0Output(map, this, indexedOutput.N, indexedOutput.TxOut));
106106
}
107107
}
108108

@@ -116,6 +116,51 @@ internal override void FillMap(Map map)
116116

117117
internal override Transaction GetGlobalTransaction(bool @unsafe) => @unsafe ? tx : tx.Clone();
118118

119+
internal class PSBT0Output : PSBTOutput
120+
{
121+
public PSBT0Output(PSBT parent, uint index, TxOut txOut) : base(parent, index)
122+
{
123+
if (txOut == null)
124+
throw new ArgumentNullException(nameof(txOut));
125+
TxOut = txOut;
126+
}
127+
128+
public PSBT0Output(Map map, PSBT parent, uint index, TxOut txOut) : base(map, parent, index)
129+
{
130+
if (txOut is null)
131+
throw new ArgumentNullException(nameof(txOut));
132+
TxOut = txOut;
133+
}
134+
135+
internal TxOut TxOut { get; }
136+
public override Script ScriptPubKey
137+
{
138+
get => TxOut.ScriptPubKey;
139+
set
140+
{
141+
if (value is null)
142+
throw new ArgumentNullException(nameof(value));
143+
TxOut.ScriptPubKey = value;
144+
((PSBT0)Parent).tx.PrecomputeHash(true, true);
145+
}
146+
}
147+
public override Money Value
148+
{
149+
get => TxOut.Value;
150+
set
151+
{
152+
if (value is null)
153+
throw new ArgumentNullException(nameof(value));
154+
TxOut.Value = value;
155+
((PSBT0)Parent).tx.PrecomputeHash(true, true);
156+
}
157+
}
158+
public override Coin? GetCoin()
159+
{
160+
var outpoint = new OutPoint(((PSBT0)Parent).tx.GetHash(), this.Index);
161+
return new Coin(outpoint, TxOut);
162+
}
163+
}
119164
internal class PSBT0Input : PSBTInput
120165
{
121166
internal PSBT0Input(Map map, PSBT0 parent, uint index) : base(map, parent, index)

NBitcoin/BIP174/PSBTInput.cs

Lines changed: 90 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -23,156 +23,101 @@ internal PSBTInput(PSBT parent, uint index) : base(parent)
2323
internal PSBTInput(Map map, PSBT parent, uint index) : base(parent)
2424
{
2525
Index = index;
26-
Load(map);
27-
}
28-
29-
protected virtual void Load(Map map)
30-
{
31-
while (map.Pop(out byte[] k, out byte[] v))
26+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_NON_WITNESS_UTXO, out var b))
3227
{
33-
34-
switch (k.First())
28+
non_witness_utxo = Parent.GetConsensusFactory().CreateTransaction();
29+
non_witness_utxo.FromBytes(b);
30+
}
31+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_WITNESS_UTXO, out b))
32+
{
33+
if (Parent.GetConsensusFactory().TryCreateNew<TxOut>(out var txout))
3534
{
36-
case PSBTConstants.PSBT_IN_NON_WITNESS_UTXO:
37-
if (k.Length != 1)
38-
throw new FormatException(
39-
"Invalid PSBTInput. Contains illegal value in key for NonWitnessUTXO");
40-
if (non_witness_utxo != null)
41-
throw new FormatException("Invalid PSBTInput. Duplicate non_witness_utxo");
42-
non_witness_utxo = Parent.GetConsensusFactory().CreateTransaction();
43-
non_witness_utxo.FromBytes(v);
44-
break;
45-
case PSBTConstants.PSBT_IN_WITNESS_UTXO:
46-
if (k.Length != 1)
47-
throw new FormatException(
48-
"Invalid PSBTInput. Contains illegal value in key for WitnessUTXO");
49-
if (witness_utxo != null)
50-
throw new FormatException("Invalid PSBTInput. Duplicate witness_utxo");
51-
if (Parent.GetConsensusFactory().TryCreateNew<TxOut>(out var txout))
52-
{
53-
witness_utxo = txout;
54-
}
55-
else
56-
{
57-
witness_utxo = new TxOut();
58-
}
35+
witness_utxo = txout;
36+
}
37+
else
38+
{
39+
witness_utxo = new TxOut();
40+
}
5941

60-
witness_utxo.FromBytes(v);
61-
break;
62-
case PSBTConstants.PSBT_IN_PARTIAL_SIG:
63-
var pkbytes = k.Skip(1).ToArray();
64-
if (pkbytes.Length == 33)
65-
{
66-
var pubkey = new PubKey(pkbytes);
67-
if (partial_sigs.ContainsKey(pubkey))
68-
throw new FormatException("Invalid PSBTInput. Duplicate key for partial_sigs");
69-
partial_sigs.Add(pubkey, new TransactionSignature(v));
70-
}
71-
else
72-
throw new FormatException("Unexpected public key size in the PSBT");
73-
74-
break;
75-
case PSBTConstants.PSBT_IN_SIGHASH:
76-
if (k.Length != 1)
77-
throw new FormatException(
78-
"Invalid PSBTInput. Contains illegal value in key for SigHash type");
79-
if (!(sighash_type is null))
80-
throw new FormatException("Invalid PSBTInput. Duplicate key for sighash_type");
81-
if (v.Length != 4)
82-
throw new FormatException("Invalid PSBTInput. SigHash Type is not 4 byte");
83-
var value = Utils.ToUInt32(v, 0, true);
84-
if (value is not (1 or 2 or 3 or 0 or 1 | 0x80 or 2 | 0x80 or 3 | 0x80 or 0 | 0x80))
85-
throw new FormatException($"Invalid PSBTInput Unknown SigHash Type {value}");
86-
sighash_type = value;
87-
break;
88-
case PSBTConstants.PSBT_IN_REDEEMSCRIPT:
89-
if (k.Length != 1)
90-
throw new FormatException(
91-
"Invalid PSBTInput. Contains illegal value in key for redeem script");
92-
if (redeem_script != null)
93-
throw new FormatException("Invalid PSBTInput. Duplicate key for redeem_script");
94-
redeem_script = Script.FromBytesUnsafe(v);
95-
break;
96-
case PSBTConstants.PSBT_IN_WITNESSSCRIPT:
97-
if (k.Length != 1)
98-
throw new FormatException(
99-
"Invalid PSBTInput. Contains illegal value in key for witness script");
100-
if (witness_script != null)
101-
throw new FormatException("Invalid PSBTInput. Duplicate key for redeem_script");
102-
witness_script = Script.FromBytesUnsafe(v);
103-
break;
104-
case PSBTConstants.PSBT_IN_TAP_KEY_SIG:
105-
if (k.Length != 1)
106-
throw new FormatException(
107-
"Invalid PSBTInput. Unexpected key length for PSBT_IN_TAP_KEY_SIG");
108-
if (!TaprootSignature.TryParse(v, out var sig))
109-
throw new FormatException("Invalid PSBTInput. Contains invalid TaprootSignature");
110-
TaprootKeySignature = sig;
111-
break;
112-
case PSBTConstants.PSBT_IN_TAP_INTERNAL_KEY:
113-
if (k.Length != 1)
114-
throw new FormatException(
115-
"Invalid PSBTInput. Unexpected key length for PSBT_IN_TAP_INTERNAL_KEY");
116-
if (!TaprootInternalPubKey.TryCreate(v, out var tpk))
117-
throw new FormatException("Invalid PSBTInput. Contains invalid internal taproot pubkey");
118-
TaprootInternalKey = tpk;
119-
break;
120-
case PSBTConstants.PSBT_IN_BIP32_DERIVATION:
121-
var pubkey2 = new PubKey(k.Skip(1).ToArray());
122-
if (hd_keypaths.ContainsKey(pubkey2))
123-
throw new FormatException("Invalid PSBTInput. Duplicate key for hd_keypaths");
124-
var masterFingerPrint = new HDFingerprint(v.Take(4).ToArray());
125-
KeyPath path = KeyPath.FromBytes(v.Skip(4).ToArray());
126-
hd_keypaths.Add(pubkey2, new RootedKeyPath(masterFingerPrint, path));
127-
break;
128-
case PSBTConstants.PSBT_IN_TAP_BIP32_DERIVATION:
129-
var pubkey3 = new TaprootPubKey(k.Skip(1).ToArray());
130-
if (hd_taprootkeypaths.ContainsKey(pubkey3))
131-
throw new FormatException(
132-
"Invalid PSBTOutput, duplicate key for PSBT_IN_TAP_BIP32_DERIVATION");
133-
var bs = new BitcoinStream(v);
134-
List<uint256> hashes = null!;
135-
bs.ReadWrite(ref hashes);
136-
var pos = (int)bs.Inner.Position;
137-
KeyPath path2 = KeyPath.FromBytes(v.Skip(pos + 4).ToArray());
138-
hd_taprootkeypaths.Add(pubkey3,
139-
new TaprootKeyPath(
140-
new RootedKeyPath(new HDFingerprint(v.Skip(pos).Take(4).ToArray()), path2),
141-
hashes.ToArray()));
142-
break;
143-
case PSBTConstants.PSBT_IN_TAP_MERKLE_ROOT:
144-
if (k.Length != 1)
145-
throw new FormatException(
146-
"Invalid PSBTInput. Unexpected key length for PSBT_IN_TAP_MERKLE_ROOT");
147-
if (v.Length != 32)
148-
throw new FormatException(
149-
"Invalid PSBTInput. Unexpected value length for PSBT_IN_TAP_MERKLE_ROOT");
150-
TaprootMerkleRoot = new uint256(v);
151-
break;
152-
case PSBTConstants.PSBT_IN_SCRIPTSIG:
153-
if (k.Length != 1)
154-
throw new FormatException(
155-
"Invalid PSBTInput. Contains illegal value in key for final scriptsig");
156-
if (FinalScriptSig != null)
157-
throw new FormatException("Invalid PSBTInput. Duplicate key for final_script_sig");
158-
FinalScriptSig = Script.FromBytesUnsafe(v);
159-
break;
160-
case PSBTConstants.PSBT_IN_SCRIPTWITNESS:
161-
if (k.Length != 1)
162-
throw new FormatException(
163-
"Invalid PSBTInput. Contains illegal value in key for final script witness");
164-
if (FinalScriptWitness != null)
165-
throw new FormatException("Invalid PSBTInput. Duplicate key for final_script_witness");
166-
FinalScriptWitness = new WitScript(v);
167-
break;
168-
default:
169-
if (unknown.ContainsKey(k))
170-
throw new FormatException("Invalid PSBTInput. Duplicate key for unknown value");
171-
unknown.Add(k, v);
172-
break;
42+
witness_utxo.FromBytes(b);
43+
}
44+
45+
foreach (var kv in map.RemoveAll<byte[]>(PSBTConstants.PSBT_IN_PARTIAL_SIG))
46+
{
47+
var pkbytes = kv.Key.Skip(1).ToArray();
48+
if (pkbytes.Length == 33)
49+
{
50+
var pubkey = new PubKey(pkbytes);
51+
if (partial_sigs.ContainsKey(pubkey))
52+
throw new FormatException("Invalid PSBTInput. Duplicate key for partial_sigs");
53+
partial_sigs.Add(pubkey, new TransactionSignature(kv.Value));
17354
}
55+
else
56+
throw new FormatException("Unexpected public key size in the PSBT");
57+
}
17458

59+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_SIGHASH, out b))
60+
{
61+
if (b.Length != 4)
62+
throw new FormatException("Invalid PSBTInput. SigHash Type is not 4 byte");
63+
var value = Utils.ToUInt32(b, 0, true);
64+
if (value is not (1 or 2 or 3 or 0 or 1 | 0x80 or 2 | 0x80 or 3 | 0x80 or 0 | 0x80))
65+
throw new FormatException($"Invalid PSBTInput Unknown SigHash Type {value}");
66+
sighash_type = value;
67+
}
68+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_REDEEMSCRIPT, out b))
69+
redeem_script = Script.FromBytesUnsafe(b);
70+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_WITNESSSCRIPT, out b))
71+
witness_script = Script.FromBytesUnsafe(b);
72+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_TAP_KEY_SIG, out b))
73+
{
74+
if (!TaprootSignature.TryParse(b, out var sig))
75+
throw new FormatException("Invalid PSBTInput. Contains invalid TaprootSignature");
76+
TaprootKeySignature = sig;
77+
}
78+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_TAP_INTERNAL_KEY, out b))
79+
{
80+
if (!TaprootInternalPubKey.TryCreate(b, out var tpk))
81+
throw new FormatException("Invalid PSBTInput. Contains invalid internal taproot pubkey");
82+
TaprootInternalKey = tpk;
83+
}
84+
foreach (var kv in map.RemoveAll<byte[]>(PSBTConstants.PSBT_IN_BIP32_DERIVATION))
85+
{
86+
var pubkey2 = new PubKey(kv.Key.Skip(1).ToArray());
87+
if (hd_keypaths.ContainsKey(pubkey2))
88+
throw new FormatException("Invalid PSBTInput. Duplicate key for hd_keypaths");
89+
var masterFingerPrint = new HDFingerprint(kv.Value.Take(4).ToArray());
90+
KeyPath path = KeyPath.FromBytes(kv.Value.Skip(4).ToArray());
91+
hd_keypaths.Add(pubkey2, new RootedKeyPath(masterFingerPrint, path));
92+
}
93+
foreach (var kv in map.RemoveAll<byte[]>(PSBTConstants.PSBT_IN_TAP_BIP32_DERIVATION))
94+
{
95+
var pubkey3 = new TaprootPubKey(kv.Key.Skip(1).ToArray());
96+
if (hd_taprootkeypaths.ContainsKey(pubkey3))
97+
throw new FormatException(
98+
"Invalid PSBTOutput, duplicate key for PSBT_IN_TAP_BIP32_DERIVATION");
99+
var bs = new BitcoinStream(kv.Value);
100+
List<uint256> hashes = null!;
101+
bs.ReadWrite(ref hashes);
102+
var pos = (int)bs.Inner.Position;
103+
KeyPath path2 = KeyPath.FromBytes(kv.Value.Skip(pos + 4).ToArray());
104+
hd_taprootkeypaths.Add(pubkey3,
105+
new TaprootKeyPath(
106+
new RootedKeyPath(new HDFingerprint(kv.Value.Skip(pos).Take(4).ToArray()), path2),
107+
hashes.ToArray()));
108+
}
109+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_TAP_MERKLE_ROOT, out b))
110+
{
111+
if (b.Length != 32)
112+
throw new FormatException(
113+
"Invalid PSBTInput. Unexpected value length for PSBT_IN_TAP_MERKLE_ROOT");
114+
TaprootMerkleRoot = new uint256(b);
175115
}
116+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_SCRIPTSIG, out b))
117+
FinalScriptSig = Script.FromBytesUnsafe(b);
118+
if (map.TryRemove<byte[]>(PSBTConstants.PSBT_IN_SCRIPTWITNESS, out b))
119+
FinalScriptWitness = new WitScript(b);
120+
unknown = map;
176121
}
177122

178123
public abstract Sequence Sequence { get; set; }
@@ -575,7 +520,7 @@ public IList<PSBTError> CheckSanity()
575520

576521
public bool? IsNonWitnessUTXOMissing()
577522
{
578-
if (!Parent.Network.Consensus.NeverNeedPreviousTxForSigning || non_witness_utxo is not null)
523+
if (Parent.Network.Consensus.NeverNeedPreviousTxForSigning || non_witness_utxo is not null)
579524
return false;
580525
if (witness_utxo?.ScriptPubKey is not Script s)
581526
return null;

0 commit comments

Comments
 (0)