nockchain 0.1.0__tar.gz
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.
- nockchain-0.1.0/LICENSE +21 -0
- nockchain-0.1.0/MANIFEST.in +3 -0
- nockchain-0.1.0/PKG-INFO +227 -0
- nockchain-0.1.0/README.md +211 -0
- nockchain-0.1.0/SECURITY.md +30 -0
- nockchain-0.1.0/THIRD_PARTY_LICENSES +23 -0
- nockchain-0.1.0/pyproject.toml +30 -0
- nockchain-0.1.0/requirements-dev.txt +3 -0
- nockchain-0.1.0/setup.cfg +4 -0
- nockchain-0.1.0/src/nockchain/__init__.py +143 -0
- nockchain-0.1.0/src/nockchain/_constants.py +374 -0
- nockchain-0.1.0/src/nockchain/_core.py +424 -0
- nockchain-0.1.0/src/nockchain/_curve.py +140 -0
- nockchain-0.1.0/src/nockchain/_english.py +2052 -0
- nockchain-0.1.0/src/nockchain/_tree.py +134 -0
- nockchain-0.1.0/src/nockchain/keys.py +343 -0
- nockchain-0.1.0/src/nockchain/locks.py +222 -0
- nockchain-0.1.0/src/nockchain/py.typed +0 -0
- nockchain-0.1.0/src/nockchain/seed.py +119 -0
- nockchain-0.1.0/src/nockchain/transaction.py +502 -0
- nockchain-0.1.0/src/nockchain/wallet.py +249 -0
- nockchain-0.1.0/src/nockchain.egg-info/PKG-INFO +227 -0
- nockchain-0.1.0/src/nockchain.egg-info/SOURCES.txt +29 -0
- nockchain-0.1.0/src/nockchain.egg-info/dependency_links.txt +1 -0
- nockchain-0.1.0/src/nockchain.egg-info/top_level.txt +1 -0
- nockchain-0.1.0/testdata/bip39.json +146 -0
- nockchain-0.1.0/testdata/consensus.json +105 -0
- nockchain-0.1.0/testdata/reference.json +5103 -0
- nockchain-0.1.0/tests/test_boundaries.py +93 -0
- nockchain-0.1.0/tests/test_protocol.py +130 -0
- nockchain-0.1.0/tests/test_transactions.py +223 -0
nockchain-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nockchain-go contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
nockchain-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nockchain
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Native Python Nockchain V1 wallets, transactions, multisig and timelocks
|
|
5
|
+
Author: SWPSCO
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/SWPSCO/nockchain-py
|
|
8
|
+
Keywords: nockchain,nock,wallet,multisig
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Typing :: Typed
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
License-File: THIRD_PARTY_LICENSES
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# nockchain
|
|
18
|
+
|
|
19
|
+
Native Python tools for Nockchain V1 wallets and transactions. Derive receiving
|
|
20
|
+
addresses, inspect payments, select coins, and sign single-key or multisig spends.
|
|
21
|
+
Python 3.11+; the package uses the standard library and has no runtime dependencies.
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
pip install nockchain
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Receive from a public branch
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from nockchain import derive_receive_address, parse_public_key
|
|
31
|
+
|
|
32
|
+
branch = parse_public_key(zpub) # rejects private extended keys
|
|
33
|
+
receive = derive_receive_address(branch.zpub(), 42)
|
|
34
|
+
print(receive.address, receive.lock_root)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Reserve each receiving index durably before giving its address to a payer. Use a
|
|
38
|
+
dedicated non-hardened receiving branch and retain the path and allocated-index
|
|
39
|
+
range for recovery. `address` is the sending wallet's destination; `lock_root` is
|
|
40
|
+
its output lock commitment. A zpub is privacy-sensitive.
|
|
41
|
+
|
|
42
|
+
## Wallets and recovery
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from nockchain import generate_wallet, restore_wallet
|
|
46
|
+
|
|
47
|
+
key, recovery = generate_wallet("") # optional BIP39 passphrase
|
|
48
|
+
# Back up recovery.words() privately before receiving funds. Never log it.
|
|
49
|
+
restored = restore_wallet(recovery.words(), "")
|
|
50
|
+
public_branch = key.derive("m/44'/0'/0'/9000").public().zpub()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`generate_mnemonic()` creates a 24-word English BIP39 phrase. Recovery accepts
|
|
54
|
+
12–24 words, validates checksums, and normalizes Unicode with NFKD. A wrong
|
|
55
|
+
passphrase produces a different valid wallet. `generate_seed()` creates a raw
|
|
56
|
+
64-byte seed; `master(seed_bytes)` accepts one. Raw seeds have no mnemonic.
|
|
57
|
+
`zprv()`, `words()`, and `Seed.to_bytes()` explicitly export spending authority.
|
|
58
|
+
Diagnostic string and repr output redact secret-bearing objects.
|
|
59
|
+
|
|
60
|
+
Paths start with `m` and are relative to the supplied key. Apostrophes mark
|
|
61
|
+
hardened indices, which require private parents. `public()` removes the private
|
|
62
|
+
scalar. Key parsing validates canonical coordinates and the Cheetah prime-order
|
|
63
|
+
subgroup.
|
|
64
|
+
|
|
65
|
+
## Exact amounts and coin selection
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from nockchain import Payment, parse_nocks, select_transfer
|
|
69
|
+
|
|
70
|
+
nicks = parse_nocks("1.25") # 81920
|
|
71
|
+
plan = select_transfer(sender.public(), verified_unspent_notes, [
|
|
72
|
+
Payment(destination, "1.25"),
|
|
73
|
+
])
|
|
74
|
+
review = plan.summary()
|
|
75
|
+
# Present the review and obtain the wallet user's authorization before signing.
|
|
76
|
+
signed = plan.sign(sender)
|
|
77
|
+
wire = signed.jam()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Amounts, heights, digest limbs, and atoms are Python integers. Floats and booleans
|
|
81
|
+
are rejected at numeric protocol boundaries. `1 NOCK = 65,536 nicks`;
|
|
82
|
+
`parse_nocks()` rejects sub-nick decimal precision, and `format_nocks()` is exact.
|
|
83
|
+
|
|
84
|
+
`select_transfer()` chooses sufficient notes largest-first with deterministic
|
|
85
|
+
note-name ties. `prepare_transfer()` consumes every supplied record. Both merge
|
|
86
|
+
repeated recipients, estimate fees, and send change to the sender unless
|
|
87
|
+
`TransferOptions(change_address=...)` is set. A nonzero `fee_nicks` supplies an
|
|
88
|
+
explicit fee. With automatic fees, a remainder too small to pay for change
|
|
89
|
+
metadata becomes part of the fee; the review shows the exact amount.
|
|
90
|
+
These helpers support plain 1-of-1 PKH inputs. `sign_transfer_v1()` accepts explicit
|
|
91
|
+
per-input output and fee allocations.
|
|
92
|
+
|
|
93
|
+
An `UnspentNote` holds the complete note and its verified lock root:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from nockchain import NoteV1, NoteName, UnspentNote, cue, parse_digest
|
|
97
|
+
|
|
98
|
+
note = NoteV1(
|
|
99
|
+
origin_page=100,
|
|
100
|
+
name=NoteName(parse_digest(first), parse_digest(last)),
|
|
101
|
+
note_data=cue(stored_metadata_jam),
|
|
102
|
+
nicks=10_000_000,
|
|
103
|
+
)
|
|
104
|
+
unspent = UnspentNote(note, parse_digest(input_lock_root))
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Parent commitments include version, origin, name, **all stored metadata**, and
|
|
108
|
+
value. Empty metadata is `0` only when the stored record actually has none.
|
|
109
|
+
The application authenticates input existence, unspent status, and confirmations.
|
|
110
|
+
|
|
111
|
+
## Multisig, timelocks, and independent signing
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from nockchain import (
|
|
115
|
+
HeightRange, TimelockV1, LockV1, LockedPaymentV1, LockedSpendPlanV1,
|
|
116
|
+
minimum_transaction_fee_v1, prepare_transaction_v1,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
lock = LockV1(2, (alice_pkh, bob_pkh, carol_pkh), TimelockV1(
|
|
120
|
+
relative=HeightRange(min=10),
|
|
121
|
+
absolute=HeightRange(max=500_000),
|
|
122
|
+
))
|
|
123
|
+
fee = minimum_transaction_fee_v1([lock], [recipient_lock])
|
|
124
|
+
unsigned = prepare_transaction_v1([LockedSpendPlanV1(
|
|
125
|
+
note=verified_note, lock=lock, fee=fee,
|
|
126
|
+
outputs=(LockedPaymentV1(recipient_lock, verified_note.nicks - fee),),
|
|
127
|
+
)])
|
|
128
|
+
# Each signer independently reviews the same unsigned plan.
|
|
129
|
+
alice = unsigned.sign(alice_key)
|
|
130
|
+
bob = unsigned.sign(bob_key)
|
|
131
|
+
signed = alice.merge_signatures(bob)
|
|
132
|
+
signed.verify_signatures()
|
|
133
|
+
signed.validate_inputs([verified_note], chain_height)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A lock requires **exactly m signatures**, with up to 32 distinct keys. Extra
|
|
137
|
+
signatures are rejected. Timelock endpoints are inclusive; `None` is unbounded.
|
|
138
|
+
Relative heights measure age since the note's origin page. Signing does not
|
|
139
|
+
assert that a timelock is satisfied. Each signature changes the transaction ID.
|
|
140
|
+
Partial transactions travel as jam bytes; merging rejects altered plans and
|
|
141
|
+
conflicting signatures. Plans, notes, policies, and reviews are immutable.
|
|
142
|
+
|
|
143
|
+
## Inspect and validate
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from nockchain import parse_transaction_v1
|
|
147
|
+
|
|
148
|
+
tx = parse_transaction_v1(wire)
|
|
149
|
+
review = tx.summary()
|
|
150
|
+
tx.verify_signatures()
|
|
151
|
+
tx.validate_inputs(verified_notes, chain_height)
|
|
152
|
+
print(str(tx.id))
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
| Operation | What it establishes |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `parse_transaction_v1()` | Supported structure, canonical trees, proof roots, key encoding, and embedded ID |
|
|
158
|
+
| `summary()` | Committed outputs, fees, signature hashes, and present signers |
|
|
159
|
+
| `OutputV1.lock()` | Metadata authenticates the committed output policy |
|
|
160
|
+
| `verify_signatures()` | Every signature, authorization, exact threshold, and minimum fee |
|
|
161
|
+
| `validate_inputs()` | Complete supplied records, parent hashes, balances, origin heights, and timelocks |
|
|
162
|
+
|
|
163
|
+
Validation methods raise `NockchainError` on protocol failures and return `None`
|
|
164
|
+
on success. `verify_digest()` returns a boolean. Successful parsing is not proof
|
|
165
|
+
of valid signatures or chain state. The package does not fetch chain data,
|
|
166
|
+
broadcast transactions, or implement confirmation policy.
|
|
167
|
+
|
|
168
|
+
## Protocol primitives and limits
|
|
169
|
+
|
|
170
|
+
`Digest` is an immutable tuple of five field elements. `str(digest)` encodes its
|
|
171
|
+
Base58 form and `parse_digest()` validates it. `Noun` is an integer atom or an
|
|
172
|
+
immutable `Cell`. Use `atom()`, `Cell(head, tail)`, `noun_tuple()`, and `noun_list()`
|
|
173
|
+
for construction; `hash_words()`, `hash_noun()`, `jam()`, and `cue()` implement the
|
|
174
|
+
hashing and codec primitives. Atom values must be below the Goldilocks prime;
|
|
175
|
+
these are based nouns, not arbitrary-size Nock atom containers.
|
|
176
|
+
|
|
177
|
+
Noun hashing and jam/cue traversal are iterative. Shared subtrees count toward
|
|
178
|
+
logical expansion limits even when backreferences make the wire encoding small.
|
|
179
|
+
Equality memoizes visited cell pairs. General noun operations allow 1,048,576
|
|
180
|
+
logical nodes and at most 16 MiB of jam bytes.
|
|
181
|
+
|
|
182
|
+
Transactions support full axis-1 PKH proofs, optional conjunctive timelocks, and
|
|
183
|
+
ordinary output seeds. Other Merkle proofs, hashlocks, burn locks,
|
|
184
|
+
source-constrained outputs, and bridge construction are outside this API.
|
|
185
|
+
Limits are 64 inputs, 256 output seeds, 32 keys per lock, 64 metadata entries,
|
|
186
|
+
2,048 flattened metadata words per output lock, 65,536 transaction nodes, and
|
|
187
|
+
depth 256. Every input retains at least one nonzero output seed.
|
|
188
|
+
|
|
189
|
+
Fees follow the pinned mainnet V1 schedule, merging metadata per output lock and
|
|
190
|
+
rejecting conflicting values. Applications must track network rule changes.
|
|
191
|
+
Private-key arithmetic is **variable-time**; use trusted offline signing tools.
|
|
192
|
+
See [SECURITY.md](SECURITY.md).
|
|
193
|
+
|
|
194
|
+
## Develop and verify
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
python -m venv .venv
|
|
198
|
+
. .venv/bin/activate
|
|
199
|
+
pip install -r requirements-dev.txt
|
|
200
|
+
pip install -e .
|
|
201
|
+
python -m unittest discover -s tests -v
|
|
202
|
+
ruff check src tests
|
|
203
|
+
ruff format --check src tests
|
|
204
|
+
python -m build
|
|
205
|
+
python -m twine check dist/*
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The public corpus checks 35 wallet vectors, 105 signatures, 24 BIP39 vectors,
|
|
209
|
+
13 TIP5 vectors, seven noun encodings, four single-key transactions, and two
|
|
210
|
+
multisig transactions. Tests compare exact partial and signed bytes and the
|
|
211
|
+
canonical Hoon oracle's IDs, complete note commitments, and minimum fees.
|
|
212
|
+
Additional tests cover malformed input, immutable reviews, timelock boundaries,
|
|
213
|
+
metadata, fees, and selection. CI tests Python 3.11 and 3.13, then builds and
|
|
214
|
+
installs the wheel. Normal tests are offline and require no Go, Rust, or Hoon.
|
|
215
|
+
|
|
216
|
+
The protocol source of truth is
|
|
217
|
+
[zorp-corp/nockchain at `1eac020`](https://github.com/zorp-corp/nockchain/tree/1eac02083a053c104b6a1299e80a8a51447017dc).
|
|
218
|
+
The port follows
|
|
219
|
+
[nockchain-go v0.1.0](https://github.com/yapishu/nockchain-go/tree/v0.1.0).
|
|
220
|
+
Public interoperability fixtures come from
|
|
221
|
+
[tx-types at `5f9ab2b`](https://github.com/SWPSCO/tx-types/tree/5f9ab2b38f1685a908f19a0e8f18e7845b3e7f35),
|
|
222
|
+
[nockster at `d621105`](https://github.com/SWPSCO/nockster/tree/d62110560dd005827ae8a46126c9841c86db0d6a),
|
|
223
|
+
and [Trezor BIP39 vectors](https://github.com/trezor/python-mnemonic/blob/master/vectors.json).
|
|
224
|
+
Fixture generators are in the Go repository.
|
|
225
|
+
**Never send funds to public test-vector addresses.**
|
|
226
|
+
|
|
227
|
+
MIT licensed. See [THIRD_PARTY_LICENSES](THIRD_PARTY_LICENSES) for wordlist attribution.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# nockchain
|
|
2
|
+
|
|
3
|
+
Native Python tools for Nockchain V1 wallets and transactions. Derive receiving
|
|
4
|
+
addresses, inspect payments, select coins, and sign single-key or multisig spends.
|
|
5
|
+
Python 3.11+; the package uses the standard library and has no runtime dependencies.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pip install nockchain
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Receive from a public branch
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from nockchain import derive_receive_address, parse_public_key
|
|
15
|
+
|
|
16
|
+
branch = parse_public_key(zpub) # rejects private extended keys
|
|
17
|
+
receive = derive_receive_address(branch.zpub(), 42)
|
|
18
|
+
print(receive.address, receive.lock_root)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Reserve each receiving index durably before giving its address to a payer. Use a
|
|
22
|
+
dedicated non-hardened receiving branch and retain the path and allocated-index
|
|
23
|
+
range for recovery. `address` is the sending wallet's destination; `lock_root` is
|
|
24
|
+
its output lock commitment. A zpub is privacy-sensitive.
|
|
25
|
+
|
|
26
|
+
## Wallets and recovery
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from nockchain import generate_wallet, restore_wallet
|
|
30
|
+
|
|
31
|
+
key, recovery = generate_wallet("") # optional BIP39 passphrase
|
|
32
|
+
# Back up recovery.words() privately before receiving funds. Never log it.
|
|
33
|
+
restored = restore_wallet(recovery.words(), "")
|
|
34
|
+
public_branch = key.derive("m/44'/0'/0'/9000").public().zpub()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`generate_mnemonic()` creates a 24-word English BIP39 phrase. Recovery accepts
|
|
38
|
+
12–24 words, validates checksums, and normalizes Unicode with NFKD. A wrong
|
|
39
|
+
passphrase produces a different valid wallet. `generate_seed()` creates a raw
|
|
40
|
+
64-byte seed; `master(seed_bytes)` accepts one. Raw seeds have no mnemonic.
|
|
41
|
+
`zprv()`, `words()`, and `Seed.to_bytes()` explicitly export spending authority.
|
|
42
|
+
Diagnostic string and repr output redact secret-bearing objects.
|
|
43
|
+
|
|
44
|
+
Paths start with `m` and are relative to the supplied key. Apostrophes mark
|
|
45
|
+
hardened indices, which require private parents. `public()` removes the private
|
|
46
|
+
scalar. Key parsing validates canonical coordinates and the Cheetah prime-order
|
|
47
|
+
subgroup.
|
|
48
|
+
|
|
49
|
+
## Exact amounts and coin selection
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from nockchain import Payment, parse_nocks, select_transfer
|
|
53
|
+
|
|
54
|
+
nicks = parse_nocks("1.25") # 81920
|
|
55
|
+
plan = select_transfer(sender.public(), verified_unspent_notes, [
|
|
56
|
+
Payment(destination, "1.25"),
|
|
57
|
+
])
|
|
58
|
+
review = plan.summary()
|
|
59
|
+
# Present the review and obtain the wallet user's authorization before signing.
|
|
60
|
+
signed = plan.sign(sender)
|
|
61
|
+
wire = signed.jam()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Amounts, heights, digest limbs, and atoms are Python integers. Floats and booleans
|
|
65
|
+
are rejected at numeric protocol boundaries. `1 NOCK = 65,536 nicks`;
|
|
66
|
+
`parse_nocks()` rejects sub-nick decimal precision, and `format_nocks()` is exact.
|
|
67
|
+
|
|
68
|
+
`select_transfer()` chooses sufficient notes largest-first with deterministic
|
|
69
|
+
note-name ties. `prepare_transfer()` consumes every supplied record. Both merge
|
|
70
|
+
repeated recipients, estimate fees, and send change to the sender unless
|
|
71
|
+
`TransferOptions(change_address=...)` is set. A nonzero `fee_nicks` supplies an
|
|
72
|
+
explicit fee. With automatic fees, a remainder too small to pay for change
|
|
73
|
+
metadata becomes part of the fee; the review shows the exact amount.
|
|
74
|
+
These helpers support plain 1-of-1 PKH inputs. `sign_transfer_v1()` accepts explicit
|
|
75
|
+
per-input output and fee allocations.
|
|
76
|
+
|
|
77
|
+
An `UnspentNote` holds the complete note and its verified lock root:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from nockchain import NoteV1, NoteName, UnspentNote, cue, parse_digest
|
|
81
|
+
|
|
82
|
+
note = NoteV1(
|
|
83
|
+
origin_page=100,
|
|
84
|
+
name=NoteName(parse_digest(first), parse_digest(last)),
|
|
85
|
+
note_data=cue(stored_metadata_jam),
|
|
86
|
+
nicks=10_000_000,
|
|
87
|
+
)
|
|
88
|
+
unspent = UnspentNote(note, parse_digest(input_lock_root))
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Parent commitments include version, origin, name, **all stored metadata**, and
|
|
92
|
+
value. Empty metadata is `0` only when the stored record actually has none.
|
|
93
|
+
The application authenticates input existence, unspent status, and confirmations.
|
|
94
|
+
|
|
95
|
+
## Multisig, timelocks, and independent signing
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from nockchain import (
|
|
99
|
+
HeightRange, TimelockV1, LockV1, LockedPaymentV1, LockedSpendPlanV1,
|
|
100
|
+
minimum_transaction_fee_v1, prepare_transaction_v1,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
lock = LockV1(2, (alice_pkh, bob_pkh, carol_pkh), TimelockV1(
|
|
104
|
+
relative=HeightRange(min=10),
|
|
105
|
+
absolute=HeightRange(max=500_000),
|
|
106
|
+
))
|
|
107
|
+
fee = minimum_transaction_fee_v1([lock], [recipient_lock])
|
|
108
|
+
unsigned = prepare_transaction_v1([LockedSpendPlanV1(
|
|
109
|
+
note=verified_note, lock=lock, fee=fee,
|
|
110
|
+
outputs=(LockedPaymentV1(recipient_lock, verified_note.nicks - fee),),
|
|
111
|
+
)])
|
|
112
|
+
# Each signer independently reviews the same unsigned plan.
|
|
113
|
+
alice = unsigned.sign(alice_key)
|
|
114
|
+
bob = unsigned.sign(bob_key)
|
|
115
|
+
signed = alice.merge_signatures(bob)
|
|
116
|
+
signed.verify_signatures()
|
|
117
|
+
signed.validate_inputs([verified_note], chain_height)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
A lock requires **exactly m signatures**, with up to 32 distinct keys. Extra
|
|
121
|
+
signatures are rejected. Timelock endpoints are inclusive; `None` is unbounded.
|
|
122
|
+
Relative heights measure age since the note's origin page. Signing does not
|
|
123
|
+
assert that a timelock is satisfied. Each signature changes the transaction ID.
|
|
124
|
+
Partial transactions travel as jam bytes; merging rejects altered plans and
|
|
125
|
+
conflicting signatures. Plans, notes, policies, and reviews are immutable.
|
|
126
|
+
|
|
127
|
+
## Inspect and validate
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from nockchain import parse_transaction_v1
|
|
131
|
+
|
|
132
|
+
tx = parse_transaction_v1(wire)
|
|
133
|
+
review = tx.summary()
|
|
134
|
+
tx.verify_signatures()
|
|
135
|
+
tx.validate_inputs(verified_notes, chain_height)
|
|
136
|
+
print(str(tx.id))
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| Operation | What it establishes |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| `parse_transaction_v1()` | Supported structure, canonical trees, proof roots, key encoding, and embedded ID |
|
|
142
|
+
| `summary()` | Committed outputs, fees, signature hashes, and present signers |
|
|
143
|
+
| `OutputV1.lock()` | Metadata authenticates the committed output policy |
|
|
144
|
+
| `verify_signatures()` | Every signature, authorization, exact threshold, and minimum fee |
|
|
145
|
+
| `validate_inputs()` | Complete supplied records, parent hashes, balances, origin heights, and timelocks |
|
|
146
|
+
|
|
147
|
+
Validation methods raise `NockchainError` on protocol failures and return `None`
|
|
148
|
+
on success. `verify_digest()` returns a boolean. Successful parsing is not proof
|
|
149
|
+
of valid signatures or chain state. The package does not fetch chain data,
|
|
150
|
+
broadcast transactions, or implement confirmation policy.
|
|
151
|
+
|
|
152
|
+
## Protocol primitives and limits
|
|
153
|
+
|
|
154
|
+
`Digest` is an immutable tuple of five field elements. `str(digest)` encodes its
|
|
155
|
+
Base58 form and `parse_digest()` validates it. `Noun` is an integer atom or an
|
|
156
|
+
immutable `Cell`. Use `atom()`, `Cell(head, tail)`, `noun_tuple()`, and `noun_list()`
|
|
157
|
+
for construction; `hash_words()`, `hash_noun()`, `jam()`, and `cue()` implement the
|
|
158
|
+
hashing and codec primitives. Atom values must be below the Goldilocks prime;
|
|
159
|
+
these are based nouns, not arbitrary-size Nock atom containers.
|
|
160
|
+
|
|
161
|
+
Noun hashing and jam/cue traversal are iterative. Shared subtrees count toward
|
|
162
|
+
logical expansion limits even when backreferences make the wire encoding small.
|
|
163
|
+
Equality memoizes visited cell pairs. General noun operations allow 1,048,576
|
|
164
|
+
logical nodes and at most 16 MiB of jam bytes.
|
|
165
|
+
|
|
166
|
+
Transactions support full axis-1 PKH proofs, optional conjunctive timelocks, and
|
|
167
|
+
ordinary output seeds. Other Merkle proofs, hashlocks, burn locks,
|
|
168
|
+
source-constrained outputs, and bridge construction are outside this API.
|
|
169
|
+
Limits are 64 inputs, 256 output seeds, 32 keys per lock, 64 metadata entries,
|
|
170
|
+
2,048 flattened metadata words per output lock, 65,536 transaction nodes, and
|
|
171
|
+
depth 256. Every input retains at least one nonzero output seed.
|
|
172
|
+
|
|
173
|
+
Fees follow the pinned mainnet V1 schedule, merging metadata per output lock and
|
|
174
|
+
rejecting conflicting values. Applications must track network rule changes.
|
|
175
|
+
Private-key arithmetic is **variable-time**; use trusted offline signing tools.
|
|
176
|
+
See [SECURITY.md](SECURITY.md).
|
|
177
|
+
|
|
178
|
+
## Develop and verify
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
python -m venv .venv
|
|
182
|
+
. .venv/bin/activate
|
|
183
|
+
pip install -r requirements-dev.txt
|
|
184
|
+
pip install -e .
|
|
185
|
+
python -m unittest discover -s tests -v
|
|
186
|
+
ruff check src tests
|
|
187
|
+
ruff format --check src tests
|
|
188
|
+
python -m build
|
|
189
|
+
python -m twine check dist/*
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The public corpus checks 35 wallet vectors, 105 signatures, 24 BIP39 vectors,
|
|
193
|
+
13 TIP5 vectors, seven noun encodings, four single-key transactions, and two
|
|
194
|
+
multisig transactions. Tests compare exact partial and signed bytes and the
|
|
195
|
+
canonical Hoon oracle's IDs, complete note commitments, and minimum fees.
|
|
196
|
+
Additional tests cover malformed input, immutable reviews, timelock boundaries,
|
|
197
|
+
metadata, fees, and selection. CI tests Python 3.11 and 3.13, then builds and
|
|
198
|
+
installs the wheel. Normal tests are offline and require no Go, Rust, or Hoon.
|
|
199
|
+
|
|
200
|
+
The protocol source of truth is
|
|
201
|
+
[zorp-corp/nockchain at `1eac020`](https://github.com/zorp-corp/nockchain/tree/1eac02083a053c104b6a1299e80a8a51447017dc).
|
|
202
|
+
The port follows
|
|
203
|
+
[nockchain-go v0.1.0](https://github.com/yapishu/nockchain-go/tree/v0.1.0).
|
|
204
|
+
Public interoperability fixtures come from
|
|
205
|
+
[tx-types at `5f9ab2b`](https://github.com/SWPSCO/tx-types/tree/5f9ab2b38f1685a908f19a0e8f18e7845b3e7f35),
|
|
206
|
+
[nockster at `d621105`](https://github.com/SWPSCO/nockster/tree/d62110560dd005827ae8a46126c9841c86db0d6a),
|
|
207
|
+
and [Trezor BIP39 vectors](https://github.com/trezor/python-mnemonic/blob/master/vectors.json).
|
|
208
|
+
Fixture generators are in the Go repository.
|
|
209
|
+
**Never send funds to public test-vector addresses.**
|
|
210
|
+
|
|
211
|
+
MIT licensed. See [THIRD_PARTY_LICENSES](THIRD_PARTY_LICENSES) for wordlist attribution.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Online services use a dedicated public receiving branch. Public extended keys
|
|
4
|
+
are privacy-sensitive. A non-hardened child private key together with its parent
|
|
5
|
+
zpub can expose the parent's private key.
|
|
6
|
+
|
|
7
|
+
Cheetah arithmetic uses variable-time Python integer operations. Private
|
|
8
|
+
operations belong in trusted offline wallet tools, not remote signing or key
|
|
9
|
+
services. Python's allocator and garbage collector do not guarantee erasure of
|
|
10
|
+
secret copies. `zprv()`, `words()`, and `Seed.to_bytes()` explicitly export spending
|
|
11
|
+
authority. Diagnostic string/repr output is redacted; generic object serializers
|
|
12
|
+
and private attributes are not secret-storage boundaries. Callers own logging,
|
|
13
|
+
backups, storage, and access control.
|
|
14
|
+
|
|
15
|
+
Nouns, locks, notes and transaction reviews are immutable. Decoding has byte,
|
|
16
|
+
logical-node and depth limits. Parsing does not establish signature or chain
|
|
17
|
+
validity. Call `verify_signatures()` and `validate_inputs()` explicitly; obtain
|
|
18
|
+
complete input records, canonical height, unspent status, and confirmations from
|
|
19
|
+
a trusted chain-state source.
|
|
20
|
+
|
|
21
|
+
The supported V1 subset uses full PKH proofs with optional conjunctive timelocks.
|
|
22
|
+
This package is not a complete consensus validator, chain client, or broadcaster.
|
|
23
|
+
Fee rules are pinned to the referenced source; applications must track changes.
|
|
24
|
+
Native NOCK uses nicks (2^16 per NOCK), distinct from Base ERC-20 units.
|
|
25
|
+
|
|
26
|
+
Tests use public Hoon, Rust and BIP39 fixtures. No independent cryptographic audit
|
|
27
|
+
is claimed. Report vulnerabilities privately through GitHub private reporting
|
|
28
|
+
when available or another established private channel to the maintainers. Never
|
|
29
|
+
include real seeds, private extended keys, API credentials, or identifying wallet
|
|
30
|
+
data in issues or reproductions.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
English BIP39 wordlist from github.com/tyler-smith/go-bip39
|
|
2
|
+
|
|
3
|
+
The MIT License (MIT)
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2014-2018 Tyler Smith and contributors
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nockchain"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Native Python Nockchain V1 wallets, transactions, multisig and timelocks"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE", "THIRD_PARTY_LICENSES"]
|
|
13
|
+
authors = [{name = "SWPSCO"}]
|
|
14
|
+
keywords = ["nockchain", "nock", "wallet", "multisig"]
|
|
15
|
+
classifiers = ["Programming Language :: Python :: 3", "Typing :: Typed"]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Repository = "https://github.com/SWPSCO/nockchain-py"
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
where = ["src"]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.package-data]
|
|
24
|
+
nockchain = ["py.typed"]
|
|
25
|
+
|
|
26
|
+
[tool.ruff]
|
|
27
|
+
line-length = 100
|
|
28
|
+
|
|
29
|
+
[tool.ruff.lint]
|
|
30
|
+
select = ["E4", "E7", "E9", "F"]
|