warnet 0.9.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- test_framework/__init__.py +0 -0
- test_framework/address.py +227 -0
- test_framework/authproxy.py +189 -0
- test_framework/bdb.py +151 -0
- test_framework/bip340_test_vectors.csv +16 -0
- test_framework/blockfilter.py +49 -0
- test_framework/blocktools.py +236 -0
- test_framework/coverage.py +113 -0
- test_framework/descriptors.py +64 -0
- test_framework/ellswift.py +163 -0
- test_framework/ellswift_decode_test_vectors.csv +77 -0
- test_framework/key.py +351 -0
- test_framework/messages.py +1906 -0
- test_framework/muhash.py +110 -0
- test_framework/netutil.py +160 -0
- test_framework/p2p.py +807 -0
- test_framework/psbt.py +140 -0
- test_framework/ripemd160.py +130 -0
- test_framework/script.py +928 -0
- test_framework/script_util.py +127 -0
- test_framework/secp256k1.py +346 -0
- test_framework/segwit_addr.py +141 -0
- test_framework/siphash.py +65 -0
- test_framework/socks5.py +162 -0
- test_framework/test_framework.py +1010 -0
- test_framework/test_node.py +892 -0
- test_framework/test_shell.py +78 -0
- test_framework/util.py +551 -0
- test_framework/wallet.py +426 -0
- test_framework/wallet_util.py +143 -0
- test_framework/xswiftec_inv_test_vectors.csv +33 -0
- warnet/__init__.py +0 -0
- warnet/backend/__init__.py +0 -0
- warnet/backend/kubernetes_backend.py +881 -0
- warnet/cli/__init__.py +0 -0
- warnet/cli/bitcoin.py +55 -0
- warnet/cli/cluster.py +193 -0
- warnet/cli/graph.py +127 -0
- warnet/cli/image.py +28 -0
- warnet/cli/image_build.py +97 -0
- warnet/cli/ln.py +39 -0
- warnet/cli/main.py +98 -0
- warnet/cli/network.py +159 -0
- warnet/cli/rpc.py +35 -0
- warnet/cli/scenarios.py +110 -0
- warnet/cln.py +196 -0
- warnet/graphs/__init__.py +3 -0
- warnet/graphs/default.graphml +81 -0
- warnet/lnchannel.py +148 -0
- warnet/lnd.py +191 -0
- warnet/lnnode.py +99 -0
- warnet/logging_config/config.json +64 -0
- warnet/scenarios/__init__.py +0 -0
- warnet/scenarios/ln_init.py +186 -0
- warnet/scenarios/miner_std.py +78 -0
- warnet/scenarios/sens_relay.py +43 -0
- warnet/scenarios/tx_flood.py +71 -0
- warnet/scenarios/utils.py +5 -0
- warnet/schema/__init__.py +3 -0
- warnet/schema/graph_schema.json +80 -0
- warnet/scripts/quick_start.sh +179 -0
- warnet/server.py +606 -0
- warnet/services.py +36 -0
- warnet/status.py +9 -0
- warnet/tank.py +194 -0
- warnet/templates/Dockerfile +88 -0
- warnet/templates/Dockerfile_sidecar +12 -0
- warnet/templates/__init__.py +3 -0
- warnet/templates/addrman.patch +51 -0
- warnet/templates/addrman_observer_config.toml +8 -0
- warnet/templates/archive/fluent.conf +28 -0
- warnet/templates/docker_entrypoint.sh +47 -0
- warnet/templates/entrypoint.sh +43 -0
- warnet/templates/fork_observer_config.toml +31 -0
- warnet/templates/grafana-provisioning/dashboards/10619_rev1.json +1782 -0
- warnet/templates/grafana-provisioning/dashboards/10619_rev2.json +1260 -0
- warnet/templates/grafana-provisioning/dashboards/bitcoin_rpc_scraper.json +340 -0
- warnet/templates/grafana-provisioning/dashboards/dashboards.yml +11 -0
- warnet/templates/grafana-provisioning/datasources/loki.yml +11 -0
- warnet/templates/grafana-provisioning/datasources/prometheus.yml +10 -0
- warnet/templates/isroutable.patch +14 -0
- warnet/templates/k8s/connect_logging.sh +8 -0
- warnet/templates/k8s/grafana/values.yaml +13 -0
- warnet/templates/k8s/install_logging.sh +13 -0
- warnet/templates/k8s/loki/values.yaml +15 -0
- warnet/templates/rpc/Dockerfile_rpc +29 -0
- warnet/templates/rpc/Dockerfile_rpc.dockerignore +16 -0
- warnet/templates/rpc/Dockerfile_rpc_dev +32 -0
- warnet/templates/rpc/entrypoint.sh +38 -0
- warnet/templates/rpc/livenessProbe.sh +7 -0
- warnet/templates/rpc/namespace.yaml +13 -0
- warnet/templates/rpc/rbac-config.yaml +60 -0
- warnet/templates/rpc/warnet-rpc-service.yaml +21 -0
- warnet/templates/rpc/warnet-rpc-statefulset-dev.yaml +49 -0
- warnet/templates/rpc/warnet-rpc-statefulset.yaml +23 -0
- warnet/templates/tor/Dockerfile_tor_da +18 -0
- warnet/templates/tor/Dockerfile_tor_relay +17 -0
- warnet/templates/tor/tor-entrypoint.sh +7 -0
- warnet/templates/tor/tor-keys/authority_certificate +45 -0
- warnet/templates/tor/tor-keys/authority_identity_key +41 -0
- warnet/templates/tor/tor-keys/authority_signing_key +27 -0
- warnet/templates/tor/tor-keys/ed25519_master_id_public_key +0 -0
- warnet/templates/tor/tor-keys/ed25519_master_id_secret_key +0 -0
- warnet/templates/tor/tor-keys/ed25519_signing_cert +0 -0
- warnet/templates/tor/tor-keys/ed25519_signing_secret_key +0 -0
- warnet/templates/tor/tor-keys/secret_id_key +15 -0
- warnet/templates/tor/tor-keys/secret_onion_key +15 -0
- warnet/templates/tor/tor-keys/secret_onion_key_ntor +0 -0
- warnet/templates/tor/torrc +34 -0
- warnet/templates/tor/torrc.da +41 -0
- warnet/templates/tor/torrc.relay +37 -0
- warnet/test_framework_bridge.py +406 -0
- warnet/utils.py +481 -0
- warnet/warnet.py +286 -0
- warnet-0.9.9.dist-info/LICENSE +21 -0
- warnet-0.9.9.dist-info/METADATA +88 -0
- warnet-0.9.9.dist-info/RECORD +120 -0
- warnet-0.9.9.dist-info/WHEEL +5 -0
- warnet-0.9.9.dist-info/entry_points.txt +3 -0
- warnet-0.9.9.dist-info/top_level.txt +2 -0
|
File without changes
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2016-2022 The Bitcoin Core developers
|
|
3
|
+
# Distributed under the MIT software license, see the accompanying
|
|
4
|
+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
+
"""Encode and decode Bitcoin addresses.
|
|
6
|
+
|
|
7
|
+
- base58 P2PKH and P2SH addresses.
|
|
8
|
+
- bech32 segwit v0 P2WPKH and P2WSH addresses.
|
|
9
|
+
- bech32m segwit v1 P2TR addresses."""
|
|
10
|
+
|
|
11
|
+
import enum
|
|
12
|
+
import unittest
|
|
13
|
+
|
|
14
|
+
from .script import (
|
|
15
|
+
CScript,
|
|
16
|
+
OP_0,
|
|
17
|
+
OP_TRUE,
|
|
18
|
+
hash160,
|
|
19
|
+
hash256,
|
|
20
|
+
sha256,
|
|
21
|
+
taproot_construct,
|
|
22
|
+
)
|
|
23
|
+
from .util import assert_equal
|
|
24
|
+
from test_framework.script_util import (
|
|
25
|
+
keyhash_to_p2pkh_script,
|
|
26
|
+
program_to_witness_script,
|
|
27
|
+
scripthash_to_p2sh_script,
|
|
28
|
+
)
|
|
29
|
+
from test_framework.segwit_addr import (
|
|
30
|
+
decode_segwit_address,
|
|
31
|
+
encode_segwit_address,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
|
|
36
|
+
ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR = 'addr(bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj)#juyq9d97'
|
|
37
|
+
# Coins sent to this address can be spent with a witness stack of just OP_TRUE
|
|
38
|
+
ADDRESS_BCRT1_P2WSH_OP_TRUE = 'bcrt1qft5p2uhsdcdc3l2ua4ap5qqfg4pjaqlp250x7us7a8qqhrxrxfsqseac85'
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AddressType(enum.Enum):
|
|
42
|
+
bech32 = 'bech32'
|
|
43
|
+
p2sh_segwit = 'p2sh-segwit'
|
|
44
|
+
legacy = 'legacy' # P2PKH
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def create_deterministic_address_bcrt1_p2tr_op_true():
|
|
51
|
+
"""
|
|
52
|
+
Generates a deterministic bech32m address (segwit v1 output) that
|
|
53
|
+
can be spent with a witness stack of OP_TRUE and the control block
|
|
54
|
+
with internal public key (script-path spending).
|
|
55
|
+
|
|
56
|
+
Returns a tuple with the generated address and the internal key.
|
|
57
|
+
"""
|
|
58
|
+
internal_key = (1).to_bytes(32, 'big')
|
|
59
|
+
address = output_key_to_p2tr(taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).output_pubkey)
|
|
60
|
+
assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')
|
|
61
|
+
return (address, internal_key)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def byte_to_base58(b, version):
|
|
65
|
+
result = ''
|
|
66
|
+
b = bytes([version]) + b # prepend version
|
|
67
|
+
b += hash256(b)[:4] # append checksum
|
|
68
|
+
value = int.from_bytes(b, 'big')
|
|
69
|
+
while value > 0:
|
|
70
|
+
result = b58chars[value % 58] + result
|
|
71
|
+
value //= 58
|
|
72
|
+
while b[0] == 0:
|
|
73
|
+
result = b58chars[0] + result
|
|
74
|
+
b = b[1:]
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def base58_to_byte(s):
|
|
79
|
+
"""Converts a base58-encoded string to its data and version.
|
|
80
|
+
|
|
81
|
+
Throws if the base58 checksum is invalid."""
|
|
82
|
+
if not s:
|
|
83
|
+
return b''
|
|
84
|
+
n = 0
|
|
85
|
+
for c in s:
|
|
86
|
+
n *= 58
|
|
87
|
+
assert c in b58chars
|
|
88
|
+
digit = b58chars.index(c)
|
|
89
|
+
n += digit
|
|
90
|
+
h = '%x' % n
|
|
91
|
+
if len(h) % 2:
|
|
92
|
+
h = '0' + h
|
|
93
|
+
res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
|
|
94
|
+
pad = 0
|
|
95
|
+
for c in s:
|
|
96
|
+
if c == b58chars[0]:
|
|
97
|
+
pad += 1
|
|
98
|
+
else:
|
|
99
|
+
break
|
|
100
|
+
res = b'\x00' * pad + res
|
|
101
|
+
|
|
102
|
+
if hash256(res[:-4])[:4] != res[-4:]:
|
|
103
|
+
raise ValueError('Invalid Base58Check checksum')
|
|
104
|
+
|
|
105
|
+
return res[1:-4], int(res[0])
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def keyhash_to_p2pkh(hash, main=False):
|
|
109
|
+
assert len(hash) == 20
|
|
110
|
+
version = 0 if main else 111
|
|
111
|
+
return byte_to_base58(hash, version)
|
|
112
|
+
|
|
113
|
+
def scripthash_to_p2sh(hash, main=False):
|
|
114
|
+
assert len(hash) == 20
|
|
115
|
+
version = 5 if main else 196
|
|
116
|
+
return byte_to_base58(hash, version)
|
|
117
|
+
|
|
118
|
+
def key_to_p2pkh(key, main=False):
|
|
119
|
+
key = check_key(key)
|
|
120
|
+
return keyhash_to_p2pkh(hash160(key), main)
|
|
121
|
+
|
|
122
|
+
def script_to_p2sh(script, main=False):
|
|
123
|
+
script = check_script(script)
|
|
124
|
+
return scripthash_to_p2sh(hash160(script), main)
|
|
125
|
+
|
|
126
|
+
def key_to_p2sh_p2wpkh(key, main=False):
|
|
127
|
+
key = check_key(key)
|
|
128
|
+
p2shscript = CScript([OP_0, hash160(key)])
|
|
129
|
+
return script_to_p2sh(p2shscript, main)
|
|
130
|
+
|
|
131
|
+
def program_to_witness(version, program, main=False):
|
|
132
|
+
if (type(program) is str):
|
|
133
|
+
program = bytes.fromhex(program)
|
|
134
|
+
assert 0 <= version <= 16
|
|
135
|
+
assert 2 <= len(program) <= 40
|
|
136
|
+
assert version > 0 or len(program) in [20, 32]
|
|
137
|
+
return encode_segwit_address("bc" if main else "bcrt", version, program)
|
|
138
|
+
|
|
139
|
+
def script_to_p2wsh(script, main=False):
|
|
140
|
+
script = check_script(script)
|
|
141
|
+
return program_to_witness(0, sha256(script), main)
|
|
142
|
+
|
|
143
|
+
def key_to_p2wpkh(key, main=False):
|
|
144
|
+
key = check_key(key)
|
|
145
|
+
return program_to_witness(0, hash160(key), main)
|
|
146
|
+
|
|
147
|
+
def script_to_p2sh_p2wsh(script, main=False):
|
|
148
|
+
script = check_script(script)
|
|
149
|
+
p2shscript = CScript([OP_0, sha256(script)])
|
|
150
|
+
return script_to_p2sh(p2shscript, main)
|
|
151
|
+
|
|
152
|
+
def output_key_to_p2tr(key, main=False):
|
|
153
|
+
assert len(key) == 32
|
|
154
|
+
return program_to_witness(1, key, main)
|
|
155
|
+
|
|
156
|
+
def check_key(key):
|
|
157
|
+
if (type(key) is str):
|
|
158
|
+
key = bytes.fromhex(key) # Assuming this is hex string
|
|
159
|
+
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
|
|
160
|
+
return key
|
|
161
|
+
assert False
|
|
162
|
+
|
|
163
|
+
def check_script(script):
|
|
164
|
+
if (type(script) is str):
|
|
165
|
+
script = bytes.fromhex(script) # Assuming this is hex string
|
|
166
|
+
if (type(script) is bytes or type(script) is CScript):
|
|
167
|
+
return script
|
|
168
|
+
assert False
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def bech32_to_bytes(address):
|
|
172
|
+
hrp = address.split('1')[0]
|
|
173
|
+
if hrp not in ['bc', 'tb', 'bcrt']:
|
|
174
|
+
return (None, None)
|
|
175
|
+
version, payload = decode_segwit_address(hrp, address)
|
|
176
|
+
if version is None:
|
|
177
|
+
return (None, None)
|
|
178
|
+
return version, bytearray(payload)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def address_to_scriptpubkey(address):
|
|
182
|
+
"""Converts a given address to the corresponding output script (scriptPubKey)."""
|
|
183
|
+
version, payload = bech32_to_bytes(address)
|
|
184
|
+
if version is not None:
|
|
185
|
+
return program_to_witness_script(version, payload) # testnet segwit scriptpubkey
|
|
186
|
+
payload, version = base58_to_byte(address)
|
|
187
|
+
if version == 111: # testnet pubkey hash
|
|
188
|
+
return keyhash_to_p2pkh_script(payload)
|
|
189
|
+
elif version == 196: # testnet script hash
|
|
190
|
+
return scripthash_to_p2sh_script(payload)
|
|
191
|
+
# TODO: also support other address formats
|
|
192
|
+
else:
|
|
193
|
+
assert False
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class TestFrameworkScript(unittest.TestCase):
|
|
197
|
+
def test_base58encodedecode(self):
|
|
198
|
+
def check_base58(data, version):
|
|
199
|
+
self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))
|
|
200
|
+
|
|
201
|
+
check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 111)
|
|
202
|
+
check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 111)
|
|
203
|
+
check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 111)
|
|
204
|
+
check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
|
|
205
|
+
check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
|
|
206
|
+
check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
|
|
207
|
+
check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 0)
|
|
208
|
+
check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 0)
|
|
209
|
+
check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 0)
|
|
210
|
+
check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
|
|
211
|
+
check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
|
|
212
|
+
check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_bech32_decode(self):
|
|
216
|
+
def check_bech32_decode(payload, version):
|
|
217
|
+
hrp = "tb"
|
|
218
|
+
self.assertEqual(bech32_to_bytes(encode_segwit_address(hrp, version, payload)), (version, payload))
|
|
219
|
+
|
|
220
|
+
check_bech32_decode(bytes.fromhex('36e3e2a33f328de12e4b43c515a75fba2632ecc3'), 0)
|
|
221
|
+
check_bech32_decode(bytes.fromhex('823e9790fc1d1782321140d4f4aa61aabd5e045b'), 0)
|
|
222
|
+
check_bech32_decode(bytes.fromhex('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'), 1)
|
|
223
|
+
check_bech32_decode(bytes.fromhex('39cf8ebd95134f431c39db0220770bd127f5dd3cc103c988b7dcd577ae34e354'), 1)
|
|
224
|
+
check_bech32_decode(bytes.fromhex('708244006d27c757f6f1fc6f853b6ec26268b727866f7ce632886e34eb5839a3'), 1)
|
|
225
|
+
check_bech32_decode(bytes.fromhex('616211ab00dffe0adcb6ce258d6d3fd8cbd901e2'), 0)
|
|
226
|
+
check_bech32_decode(bytes.fromhex('b6a7c98b482d7fb21c9fa8e65692a0890410ff22'), 0)
|
|
227
|
+
check_bech32_decode(bytes.fromhex('f0c2109cb1008cfa7b5a09cc56f7267cd8e50929'), 0)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Copyright (c) 2011 Jeff Garzik
|
|
2
|
+
#
|
|
3
|
+
# Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2007 Jan-Klaas Kollhof
|
|
6
|
+
#
|
|
7
|
+
# This file is part of jsonrpc.
|
|
8
|
+
#
|
|
9
|
+
# jsonrpc is free software; you can redistribute it and/or modify
|
|
10
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
|
11
|
+
# the Free Software Foundation; either version 2.1 of the License, or
|
|
12
|
+
# (at your option) any later version.
|
|
13
|
+
#
|
|
14
|
+
# This software is distributed in the hope that it will be useful,
|
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
# GNU Lesser General Public License for more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
20
|
+
# along with this software; if not, write to the Free Software
|
|
21
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
22
|
+
"""HTTP proxy for opening RPC connection to bitcoind.
|
|
23
|
+
|
|
24
|
+
AuthServiceProxy has the following improvements over python-jsonrpc's
|
|
25
|
+
ServiceProxy class:
|
|
26
|
+
|
|
27
|
+
- HTTP connections persist for the life of the AuthServiceProxy object
|
|
28
|
+
(if server supports HTTP/1.1)
|
|
29
|
+
- sends protocol 'version', per JSON-RPC 1.1
|
|
30
|
+
- sends proper, incrementing 'id'
|
|
31
|
+
- sends Basic HTTP authentication headers
|
|
32
|
+
- parses all JSON numbers that look like floats as Decimal
|
|
33
|
+
- uses standard Python json lib
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import base64
|
|
37
|
+
import decimal
|
|
38
|
+
from http import HTTPStatus
|
|
39
|
+
import http.client
|
|
40
|
+
import json
|
|
41
|
+
import logging
|
|
42
|
+
import pathlib
|
|
43
|
+
import socket
|
|
44
|
+
import time
|
|
45
|
+
import urllib.parse
|
|
46
|
+
|
|
47
|
+
HTTP_TIMEOUT = 30
|
|
48
|
+
USER_AGENT = "AuthServiceProxy/0.1"
|
|
49
|
+
|
|
50
|
+
log = logging.getLogger("BitcoinRPC")
|
|
51
|
+
|
|
52
|
+
class JSONRPCException(Exception):
|
|
53
|
+
def __init__(self, rpc_error, http_status=None):
|
|
54
|
+
try:
|
|
55
|
+
errmsg = '%(message)s (%(code)i)' % rpc_error
|
|
56
|
+
except (KeyError, TypeError):
|
|
57
|
+
errmsg = ''
|
|
58
|
+
super().__init__(errmsg)
|
|
59
|
+
self.error = rpc_error
|
|
60
|
+
self.http_status = http_status
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def serialization_fallback(o):
|
|
64
|
+
if isinstance(o, decimal.Decimal):
|
|
65
|
+
return str(o)
|
|
66
|
+
if isinstance(o, pathlib.Path):
|
|
67
|
+
return str(o)
|
|
68
|
+
raise TypeError(repr(o) + " is not JSON serializable")
|
|
69
|
+
|
|
70
|
+
class AuthServiceProxy():
|
|
71
|
+
__id_count = 0
|
|
72
|
+
|
|
73
|
+
# ensure_ascii: escape unicode as \uXXXX, passed to json.dumps
|
|
74
|
+
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True):
|
|
75
|
+
self.__service_url = service_url
|
|
76
|
+
self._service_name = service_name
|
|
77
|
+
self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
|
|
78
|
+
self.__url = urllib.parse.urlparse(service_url)
|
|
79
|
+
user = None if self.__url.username is None else self.__url.username.encode('utf8')
|
|
80
|
+
passwd = None if self.__url.password is None else self.__url.password.encode('utf8')
|
|
81
|
+
authpair = user + b':' + passwd
|
|
82
|
+
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
|
|
83
|
+
# clamp the socket timeout, since larger values can cause an
|
|
84
|
+
# "Invalid argument" exception in Python's HTTP(S) client
|
|
85
|
+
# library on some operating systems (e.g. OpenBSD, FreeBSD)
|
|
86
|
+
self.timeout = min(timeout, 2147483)
|
|
87
|
+
self._set_conn(connection)
|
|
88
|
+
|
|
89
|
+
def __getattr__(self, name):
|
|
90
|
+
if name.startswith('__') and name.endswith('__'):
|
|
91
|
+
# Python internal stuff
|
|
92
|
+
raise AttributeError
|
|
93
|
+
if self._service_name is not None:
|
|
94
|
+
name = "%s.%s" % (self._service_name, name)
|
|
95
|
+
return AuthServiceProxy(self.__service_url, name, connection=self.__conn)
|
|
96
|
+
|
|
97
|
+
def _request(self, method, path, postdata):
|
|
98
|
+
'''
|
|
99
|
+
Do a HTTP request.
|
|
100
|
+
'''
|
|
101
|
+
headers = {'Host': self.__url.hostname,
|
|
102
|
+
'User-Agent': USER_AGENT,
|
|
103
|
+
'Authorization': self.__auth_header,
|
|
104
|
+
'Content-type': 'application/json'}
|
|
105
|
+
self.__conn.request(method, path, postdata, headers)
|
|
106
|
+
return self._get_response()
|
|
107
|
+
|
|
108
|
+
def get_request(self, *args, **argsn):
|
|
109
|
+
AuthServiceProxy.__id_count += 1
|
|
110
|
+
|
|
111
|
+
log.debug("-{}-> {} {}".format(
|
|
112
|
+
AuthServiceProxy.__id_count,
|
|
113
|
+
self._service_name,
|
|
114
|
+
json.dumps(args or argsn, default=serialization_fallback, ensure_ascii=self.ensure_ascii),
|
|
115
|
+
))
|
|
116
|
+
if args and argsn:
|
|
117
|
+
params = dict(args=args, **argsn)
|
|
118
|
+
else:
|
|
119
|
+
params = args or argsn
|
|
120
|
+
return {'version': '1.1',
|
|
121
|
+
'method': self._service_name,
|
|
122
|
+
'params': params,
|
|
123
|
+
'id': AuthServiceProxy.__id_count}
|
|
124
|
+
|
|
125
|
+
def __call__(self, *args, **argsn):
|
|
126
|
+
postdata = json.dumps(self.get_request(*args, **argsn), default=serialization_fallback, ensure_ascii=self.ensure_ascii)
|
|
127
|
+
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
|
128
|
+
if response['error'] is not None:
|
|
129
|
+
raise JSONRPCException(response['error'], status)
|
|
130
|
+
elif 'result' not in response:
|
|
131
|
+
raise JSONRPCException({
|
|
132
|
+
'code': -343, 'message': 'missing JSON-RPC result'}, status)
|
|
133
|
+
elif status != HTTPStatus.OK:
|
|
134
|
+
raise JSONRPCException({
|
|
135
|
+
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
|
|
136
|
+
else:
|
|
137
|
+
return response['result']
|
|
138
|
+
|
|
139
|
+
def batch(self, rpc_call_list):
|
|
140
|
+
postdata = json.dumps(list(rpc_call_list), default=serialization_fallback, ensure_ascii=self.ensure_ascii)
|
|
141
|
+
log.debug("--> " + postdata)
|
|
142
|
+
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
|
143
|
+
if status != HTTPStatus.OK:
|
|
144
|
+
raise JSONRPCException({
|
|
145
|
+
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
|
|
146
|
+
return response
|
|
147
|
+
|
|
148
|
+
def _get_response(self):
|
|
149
|
+
req_start_time = time.time()
|
|
150
|
+
try:
|
|
151
|
+
http_response = self.__conn.getresponse()
|
|
152
|
+
except socket.timeout:
|
|
153
|
+
raise JSONRPCException({
|
|
154
|
+
'code': -344,
|
|
155
|
+
'message': '%r RPC took longer than %f seconds. Consider '
|
|
156
|
+
'using larger timeout for calls that take '
|
|
157
|
+
'longer to return.' % (self._service_name,
|
|
158
|
+
self.__conn.timeout)})
|
|
159
|
+
if http_response is None:
|
|
160
|
+
raise JSONRPCException({
|
|
161
|
+
'code': -342, 'message': 'missing HTTP response from server'})
|
|
162
|
+
|
|
163
|
+
content_type = http_response.getheader('Content-Type')
|
|
164
|
+
if content_type != 'application/json':
|
|
165
|
+
raise JSONRPCException(
|
|
166
|
+
{'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)},
|
|
167
|
+
http_response.status)
|
|
168
|
+
|
|
169
|
+
responsedata = http_response.read().decode('utf8')
|
|
170
|
+
response = json.loads(responsedata, parse_float=decimal.Decimal)
|
|
171
|
+
elapsed = time.time() - req_start_time
|
|
172
|
+
if "error" in response and response["error"] is None:
|
|
173
|
+
log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=serialization_fallback, ensure_ascii=self.ensure_ascii)))
|
|
174
|
+
else:
|
|
175
|
+
log.debug("<-- [%.6f] %s" % (elapsed, responsedata))
|
|
176
|
+
return response, http_response.status
|
|
177
|
+
|
|
178
|
+
def __truediv__(self, relative_uri):
|
|
179
|
+
return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn)
|
|
180
|
+
|
|
181
|
+
def _set_conn(self, connection=None):
|
|
182
|
+
port = 80 if self.__url.port is None else self.__url.port
|
|
183
|
+
if connection:
|
|
184
|
+
self.__conn = connection
|
|
185
|
+
self.timeout = connection.timeout
|
|
186
|
+
elif self.__url.scheme == 'https':
|
|
187
|
+
self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=self.timeout)
|
|
188
|
+
else:
|
|
189
|
+
self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=self.timeout)
|
test_framework/bdb.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2020-2021 The Bitcoin Core developers
|
|
3
|
+
# Distributed under the MIT software license, see the accompanying
|
|
4
|
+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
+
"""
|
|
6
|
+
Utilities for working directly with the wallet's BDB database file
|
|
7
|
+
|
|
8
|
+
This is specific to the configuration of BDB used in this project:
|
|
9
|
+
- pagesize: 4096 bytes
|
|
10
|
+
- Outer database contains single subdatabase named 'main'
|
|
11
|
+
- btree
|
|
12
|
+
- btree leaf pages
|
|
13
|
+
|
|
14
|
+
Each key-value pair is two entries in a btree leaf. The first is the key, the one that follows
|
|
15
|
+
is the value. And so on. Note that the entry data is itself not in the correct order. Instead
|
|
16
|
+
entry offsets are stored in the correct order and those offsets are needed to then retrieve
|
|
17
|
+
the data itself.
|
|
18
|
+
|
|
19
|
+
Page format can be found in BDB source code dbinc/db_page.h
|
|
20
|
+
This only implements the deserialization of btree metadata pages and normal btree pages. Overflow
|
|
21
|
+
pages are not implemented but may be needed in the future if dealing with wallets with large
|
|
22
|
+
transactions.
|
|
23
|
+
|
|
24
|
+
`db_dump -da wallet.dat` is useful to see the data in a wallet.dat BDB file
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import struct
|
|
28
|
+
|
|
29
|
+
# Important constants
|
|
30
|
+
PAGESIZE = 4096
|
|
31
|
+
OUTER_META_PAGE = 0
|
|
32
|
+
INNER_META_PAGE = 2
|
|
33
|
+
|
|
34
|
+
# Page type values
|
|
35
|
+
BTREE_INTERNAL = 3
|
|
36
|
+
BTREE_LEAF = 5
|
|
37
|
+
BTREE_META = 9
|
|
38
|
+
|
|
39
|
+
# Some magic numbers for sanity checking
|
|
40
|
+
BTREE_MAGIC = 0x053162
|
|
41
|
+
DB_VERSION = 9
|
|
42
|
+
|
|
43
|
+
# Deserializes a leaf page into a dict.
|
|
44
|
+
# Btree internal pages have the same header, for those, return None.
|
|
45
|
+
# For the btree leaf pages, deserialize them and put all the data into a dict
|
|
46
|
+
def dump_leaf_page(data):
|
|
47
|
+
page_info = {}
|
|
48
|
+
page_header = data[0:26]
|
|
49
|
+
_, pgno, prev_pgno, next_pgno, entries, hf_offset, level, pg_type = struct.unpack('QIIIHHBB', page_header)
|
|
50
|
+
page_info['pgno'] = pgno
|
|
51
|
+
page_info['prev_pgno'] = prev_pgno
|
|
52
|
+
page_info['next_pgno'] = next_pgno
|
|
53
|
+
page_info['hf_offset'] = hf_offset
|
|
54
|
+
page_info['level'] = level
|
|
55
|
+
page_info['pg_type'] = pg_type
|
|
56
|
+
page_info['entry_offsets'] = struct.unpack('{}H'.format(entries), data[26:26 + entries * 2])
|
|
57
|
+
page_info['entries'] = []
|
|
58
|
+
|
|
59
|
+
if pg_type == BTREE_INTERNAL:
|
|
60
|
+
# Skip internal pages. These are the internal nodes of the btree and don't contain anything relevant to us
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
assert pg_type == BTREE_LEAF, 'A non-btree leaf page has been encountered while dumping leaves'
|
|
64
|
+
|
|
65
|
+
for i in range(0, entries):
|
|
66
|
+
offset = page_info['entry_offsets'][i]
|
|
67
|
+
entry = {'offset': offset}
|
|
68
|
+
page_data_header = data[offset:offset + 3]
|
|
69
|
+
e_len, pg_type = struct.unpack('HB', page_data_header)
|
|
70
|
+
entry['len'] = e_len
|
|
71
|
+
entry['pg_type'] = pg_type
|
|
72
|
+
entry['data'] = data[offset + 3:offset + 3 + e_len]
|
|
73
|
+
page_info['entries'].append(entry)
|
|
74
|
+
|
|
75
|
+
return page_info
|
|
76
|
+
|
|
77
|
+
# Deserializes a btree metadata page into a dict.
|
|
78
|
+
# Does a simple sanity check on the magic value, type, and version
|
|
79
|
+
def dump_meta_page(page):
|
|
80
|
+
# metadata page
|
|
81
|
+
# general metadata
|
|
82
|
+
metadata = {}
|
|
83
|
+
meta_page = page[0:72]
|
|
84
|
+
_, pgno, magic, version, pagesize, encrypt_alg, pg_type, metaflags, _, free, last_pgno, nparts, key_count, record_count, flags, uid = struct.unpack('QIIIIBBBBIIIIII20s', meta_page)
|
|
85
|
+
metadata['pgno'] = pgno
|
|
86
|
+
metadata['magic'] = magic
|
|
87
|
+
metadata['version'] = version
|
|
88
|
+
metadata['pagesize'] = pagesize
|
|
89
|
+
metadata['encrypt_alg'] = encrypt_alg
|
|
90
|
+
metadata['pg_type'] = pg_type
|
|
91
|
+
metadata['metaflags'] = metaflags
|
|
92
|
+
metadata['free'] = free
|
|
93
|
+
metadata['last_pgno'] = last_pgno
|
|
94
|
+
metadata['nparts'] = nparts
|
|
95
|
+
metadata['key_count'] = key_count
|
|
96
|
+
metadata['record_count'] = record_count
|
|
97
|
+
metadata['flags'] = flags
|
|
98
|
+
metadata['uid'] = uid.hex().encode()
|
|
99
|
+
|
|
100
|
+
assert magic == BTREE_MAGIC, 'bdb magic does not match bdb btree magic'
|
|
101
|
+
assert pg_type == BTREE_META, 'Metadata page is not a btree metadata page'
|
|
102
|
+
assert version == DB_VERSION, 'Database too new'
|
|
103
|
+
|
|
104
|
+
# btree metadata
|
|
105
|
+
btree_meta_page = page[72:512]
|
|
106
|
+
_, minkey, re_len, re_pad, root, _, crypto_magic, _, iv, chksum = struct.unpack('IIIII368sI12s16s20s', btree_meta_page)
|
|
107
|
+
metadata['minkey'] = minkey
|
|
108
|
+
metadata['re_len'] = re_len
|
|
109
|
+
metadata['re_pad'] = re_pad
|
|
110
|
+
metadata['root'] = root
|
|
111
|
+
metadata['crypto_magic'] = crypto_magic
|
|
112
|
+
metadata['iv'] = iv.hex().encode()
|
|
113
|
+
metadata['chksum'] = chksum.hex().encode()
|
|
114
|
+
|
|
115
|
+
return metadata
|
|
116
|
+
|
|
117
|
+
# Given the dict from dump_leaf_page, get the key-value pairs and put them into a dict
|
|
118
|
+
def extract_kv_pairs(page_data):
|
|
119
|
+
out = {}
|
|
120
|
+
last_key = None
|
|
121
|
+
for i, entry in enumerate(page_data['entries']):
|
|
122
|
+
# By virtue of these all being pairs, even number entries are keys, and odd are values
|
|
123
|
+
if i % 2 == 0:
|
|
124
|
+
out[entry['data']] = b''
|
|
125
|
+
last_key = entry['data']
|
|
126
|
+
else:
|
|
127
|
+
out[last_key] = entry['data']
|
|
128
|
+
return out
|
|
129
|
+
|
|
130
|
+
# Extract the key-value pairs of the BDB file given in filename
|
|
131
|
+
def dump_bdb_kv(filename):
|
|
132
|
+
# Read in the BDB file and start deserializing it
|
|
133
|
+
pages = []
|
|
134
|
+
with open(filename, 'rb') as f:
|
|
135
|
+
data = f.read(PAGESIZE)
|
|
136
|
+
while len(data) > 0:
|
|
137
|
+
pages.append(data)
|
|
138
|
+
data = f.read(PAGESIZE)
|
|
139
|
+
|
|
140
|
+
# Sanity check the meta pages
|
|
141
|
+
dump_meta_page(pages[OUTER_META_PAGE])
|
|
142
|
+
dump_meta_page(pages[INNER_META_PAGE])
|
|
143
|
+
|
|
144
|
+
# Fetch the kv pairs from the leaf pages
|
|
145
|
+
kv = {}
|
|
146
|
+
for i in range(3, len(pages)):
|
|
147
|
+
info = dump_leaf_page(pages[i])
|
|
148
|
+
if info is not None:
|
|
149
|
+
info_kv = extract_kv_pairs(info)
|
|
150
|
+
kv = {**kv, **info_kv}
|
|
151
|
+
return kv
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
index,secret key,public key,aux_rand,message,signature,verification result,comment
|
|
2
|
+
0,0000000000000000000000000000000000000000000000000000000000000003,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0,TRUE,
|
|
3
|
+
1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,0000000000000000000000000000000000000000000000000000000000000001,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A,TRUE,
|
|
4
|
+
2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9,DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8,C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906,7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7,TRUE,
|
|
5
|
+
3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3,TRUE,test fails if msg is reduced modulo p or n
|
|
6
|
+
4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4,TRUE,
|
|
7
|
+
5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key not on the curve
|
|
8
|
+
6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2,FALSE,has_even_y(R) is false
|
|
9
|
+
7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD,FALSE,negated message
|
|
10
|
+
8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6,FALSE,negated s value
|
|
11
|
+
9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0
|
|
12
|
+
10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1
|
|
13
|
+
11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is not an X coordinate on the curve
|
|
14
|
+
12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size
|
|
15
|
+
13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order
|
|
16
|
+
14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2022 The Bitcoin Core developers
|
|
3
|
+
# Distributed under the MIT software license, see the accompanying
|
|
4
|
+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
+
"""Helper routines relevant for compact block filters (BIP158).
|
|
6
|
+
"""
|
|
7
|
+
from .siphash import siphash
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def bip158_basic_element_hash(script_pub_key, N, block_hash):
|
|
11
|
+
""" Calculates the ranged hash of a filter element as defined in BIP158:
|
|
12
|
+
|
|
13
|
+
'The first step in the filter construction is hashing the variable-sized
|
|
14
|
+
raw items in the set to the range [0, F), where F = N * M.'
|
|
15
|
+
|
|
16
|
+
'The items are first passed through the pseudorandom function SipHash, which takes a
|
|
17
|
+
128-bit key k and a variable-sized byte vector and produces a uniformly random 64-bit
|
|
18
|
+
output. Implementations of this BIP MUST use the SipHash parameters c = 2 and d = 4.'
|
|
19
|
+
|
|
20
|
+
'The parameter k MUST be set to the first 16 bytes of the hash (in standard
|
|
21
|
+
little-endian representation) of the block for which the filter is constructed. This
|
|
22
|
+
ensures the key is deterministic while still varying from block to block.'
|
|
23
|
+
"""
|
|
24
|
+
M = 784931
|
|
25
|
+
block_hash_bytes = bytes.fromhex(block_hash)[::-1]
|
|
26
|
+
k0 = int.from_bytes(block_hash_bytes[0:8], 'little')
|
|
27
|
+
k1 = int.from_bytes(block_hash_bytes[8:16], 'little')
|
|
28
|
+
return (siphash(k0, k1, script_pub_key) * (N * M)) >> 64
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def bip158_relevant_scriptpubkeys(node, block_hash):
|
|
32
|
+
""" Determines the basic filter relvant scriptPubKeys as defined in BIP158:
|
|
33
|
+
|
|
34
|
+
'A basic filter MUST contain exactly the following items for each transaction in a block:
|
|
35
|
+
- The previous output script (the script being spent) for each input, except for
|
|
36
|
+
the coinbase transaction.
|
|
37
|
+
- The scriptPubKey of each output, aside from all OP_RETURN output scripts.'
|
|
38
|
+
"""
|
|
39
|
+
spks = set()
|
|
40
|
+
for tx in node.getblock(blockhash=block_hash, verbosity=3)['tx']:
|
|
41
|
+
# gather prevout scripts
|
|
42
|
+
for i in tx['vin']:
|
|
43
|
+
if 'prevout' in i:
|
|
44
|
+
spks.add(bytes.fromhex(i['prevout']['scriptPubKey']['hex']))
|
|
45
|
+
# gather output scripts
|
|
46
|
+
for o in tx['vout']:
|
|
47
|
+
if o['scriptPubKey']['type'] != 'nulldata':
|
|
48
|
+
spks.add(bytes.fromhex(o['scriptPubKey']['hex']))
|
|
49
|
+
return spks
|