tapyrus 0.3.9 → 0.4.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +2 -2
- data/.ruby-version +1 -1
- data/README.md +43 -2
- data/lib/tapyrus/ext_key.rb +8 -4
- data/lib/tapyrus/key.rb +1 -1
- data/lib/tapyrus/key_path.rb +17 -13
- data/lib/tapyrus/message/fee_filter.rb +2 -2
- data/lib/tapyrus/message/header_and_short_ids.rb +4 -4
- data/lib/tapyrus/message/inventory.rb +1 -1
- data/lib/tapyrus/message/network_addr.rb +2 -2
- data/lib/tapyrus/message/ping.rb +2 -2
- data/lib/tapyrus/message/pong.rb +2 -2
- data/lib/tapyrus/message/send_cmpct.rb +2 -2
- data/lib/tapyrus/message/version.rb +3 -3
- data/lib/tapyrus/mnemonic.rb +9 -4
- data/lib/tapyrus/pstt/input.rb +422 -0
- data/lib/tapyrus/pstt/key_origin_info.rb +63 -0
- data/lib/tapyrus/pstt/output.rb +129 -0
- data/lib/tapyrus/pstt/proprietary.rb +54 -0
- data/lib/tapyrus/pstt/tx.rb +658 -0
- data/lib/tapyrus/pstt.rb +335 -0
- data/lib/tapyrus/rpc.rb +0 -2
- data/lib/tapyrus/script/script.rb +14 -4
- data/lib/tapyrus/secp256k1/rfc6979.rb +6 -3
- data/lib/tapyrus/tip0020.rb +341 -0
- data/lib/tapyrus/tx.rb +6 -2
- data/lib/tapyrus/tx_out.rb +2 -2
- data/lib/tapyrus/util.rb +3 -3
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +2 -2
- data/lib/tapyrus/wallet/db.rb +3 -3
- data/lib/tapyrus/wallet/master_key.rb +2 -2
- data/lib/tapyrus.rb +3 -3
- data/tapyrusrb.gemspec +0 -3
- metadata +10 -61
- data/exe/tapyrusrb-cli +0 -5
- data/exe/tapyrusrbd +0 -42
- data/lib/tapyrus/network/connection.rb +0 -70
- data/lib/tapyrus/network/message_handler.rb +0 -244
- data/lib/tapyrus/network/peer.rb +0 -210
- data/lib/tapyrus/network/peer_discovery.rb +0 -43
- data/lib/tapyrus/network/pool.rb +0 -135
- data/lib/tapyrus/network.rb +0 -11
- data/lib/tapyrus/node/cli.rb +0 -114
- data/lib/tapyrus/node/configuration.rb +0 -36
- data/lib/tapyrus/node/spv.rb +0 -78
- data/lib/tapyrus/node.rb +0 -7
- data/lib/tapyrus/rpc/http_server.rb +0 -64
- data/lib/tapyrus/rpc/request_handler.rb +0 -146
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9ceb5c0fcc47319d15e66a21760c345cb1ee029a71f1313046f1676e883972a5
|
|
4
|
+
data.tar.gz: 13a2463ef2432f904acef3b9880a275d0e174485086a469d9a9177dcebf9f200
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8a89f3d1a2bca680384a69dd4793d43a4c102e67ae14e69e4d88205ef46c343e8efe015eaf1b586c17e4b1163814628425282cb3911b506397b28ee49ac66ac7
|
|
7
|
+
data.tar.gz: d819136137c7571173e98576ce21b5b08b1c5779269baddbdd49bcd4bfcffc51076e5ecee0c27ddfba56c3ca34f8c70e68e1cc3f76165fe4bdea21a6060ad0d3
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -18,11 +18,11 @@ jobs:
|
|
|
18
18
|
runs-on: ubuntu-latest
|
|
19
19
|
strategy:
|
|
20
20
|
matrix:
|
|
21
|
-
ruby-version: ["3.
|
|
21
|
+
ruby-version: ["3.2", "3.3", "3.4", "4.0"]
|
|
22
22
|
|
|
23
23
|
steps:
|
|
24
24
|
- run: sudo apt install libleveldb-dev
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v5
|
|
26
26
|
- name: Set up Ruby
|
|
27
27
|
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
|
28
28
|
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ruby-4.0.0
|
data/README.md
CHANGED
|
@@ -13,8 +13,8 @@ Tapyrusrb supports following feature:
|
|
|
13
13
|
- De/serialization of blocks and transactions
|
|
14
14
|
- Key generation and verification for Schnorr and ECDSA (including [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) supports).
|
|
15
15
|
- ECDSA signature(RFC6979 -Deterministic ECDSA, LOW-S, LOW-R support)
|
|
16
|
-
-
|
|
17
|
-
- [
|
|
16
|
+
- Schnorr signature
|
|
17
|
+
- Partially Signed Tapyrus Transaction ([TIP-0174](https://github.com/chaintope/tips/blob/master/tip-0174.md))
|
|
18
18
|
|
|
19
19
|
## Requirements
|
|
20
20
|
|
|
@@ -80,6 +80,47 @@ Tapyrus.chain_params = :dev
|
|
|
80
80
|
|
|
81
81
|
This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/dev.yml.
|
|
82
82
|
|
|
83
|
+
### PSTT (Partially Signed Tapyrus Transaction)
|
|
84
|
+
|
|
85
|
+
`Tapyrus::PSTT` implements the format [TIP-0174](https://github.com/chaintope/tips/blob/master/tip-0174.md) defines.
|
|
86
|
+
Its methods follow the roles of the TIP: Creator, Constructor, Updater, Signer, Combiner, Input Finalizer and
|
|
87
|
+
Transaction Extractor.
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
# Creator
|
|
91
|
+
pstt =
|
|
92
|
+
Tapyrus::PSTT::Tx.new(
|
|
93
|
+
inputs: [Tapyrus::PSTT::Input.new(out_point: out_point, utxo: prev_tx)],
|
|
94
|
+
outputs: [Tapyrus::PSTT::Output.new(amount: 99_000, script_pubkey: script_pubkey)]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Signer. The signature hash is computed from the fields of the PSTT.
|
|
98
|
+
pstt.sign(0, key) # ECDSA. Pass algo: :schnorr for a Schnorr signature.
|
|
99
|
+
|
|
100
|
+
# Input Finalizer and Transaction Extractor
|
|
101
|
+
pstt.finalize!
|
|
102
|
+
tx = pstt.extract_tx
|
|
103
|
+
|
|
104
|
+
# For text transport
|
|
105
|
+
Tapyrus::PSTT::Tx.from_base64(pstt.to_base64)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
A PSTT which other parties will extend is created with `tx_modifiable`, so that a Constructor can add inputs and
|
|
109
|
+
outputs afterwards. This is what a fee provider, which supplies the TPC that pays the fee of a Colored Coin
|
|
110
|
+
transaction, needs:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# The user leaves the inputs modifiable and signs with SIGHASH_ALL | SIGHASH_ANYONECANPAY.
|
|
114
|
+
pstt = Tapyrus::PSTT::Tx.new(tx_modifiable: Tapyrus::PSTT::TxModifiable::INPUTS, inputs: inputs, outputs: outputs)
|
|
115
|
+
pstt.sign(0, key, sighash_type: Tapyrus::SIGHASH_TYPE[:all] | Tapyrus::SIGHASH_TYPE[:anyonecanpay])
|
|
116
|
+
|
|
117
|
+
# The fee provider verifies the amounts per color, then adds its TPC input and signs with SIGHASH_ALL.
|
|
118
|
+
pstt.input_amounts # => { Tapyrus::Color::ColorIdentifier => amount }
|
|
119
|
+
pstt.fee
|
|
120
|
+
pstt.add_input(Tapyrus::PSTT::Input.new(out_point: fee_out_point, utxo: fee_tx))
|
|
121
|
+
pstt.sign(1, provider_key)
|
|
122
|
+
```
|
|
123
|
+
|
|
83
124
|
## Contributing
|
|
84
125
|
|
|
85
126
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/tapyrusrb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/lib/tapyrus/ext_key.rb
CHANGED
|
@@ -111,14 +111,16 @@ module Tapyrus
|
|
|
111
111
|
child_priv = ECDSA::Format::IntegerOctetString.encode(child_priv, 32)
|
|
112
112
|
new_key.key = Tapyrus::Key.new(priv_key: child_priv.bth, key_type: key_type)
|
|
113
113
|
new_key.chain_code = l[32..-1]
|
|
114
|
-
|
|
114
|
+
# A depth 1 key derives its version bytes from the purpose, see #version.
|
|
115
|
+
new_key.ver = new_key.depth == 1 ? nil : version
|
|
115
116
|
new_key
|
|
116
117
|
end
|
|
117
118
|
|
|
118
119
|
# get version bytes using serialization format
|
|
119
120
|
def version
|
|
121
|
+
return ver if ver
|
|
120
122
|
return ExtKey.version_from_purpose(number) if depth == 1
|
|
121
|
-
|
|
123
|
+
Tapyrus.chain_params.extended_privkey_version
|
|
122
124
|
end
|
|
123
125
|
|
|
124
126
|
# get key type defined by BIP-178 using version.
|
|
@@ -282,14 +284,16 @@ module Tapyrus
|
|
|
282
284
|
p2 = Tapyrus::Key.new(pubkey: pubkey, key_type: key_type).to_point
|
|
283
285
|
new_key.pubkey = (p1 + p2).to_hex
|
|
284
286
|
new_key.chain_code = l[32..-1]
|
|
285
|
-
|
|
287
|
+
# A depth 1 key derives its version bytes from the purpose, see #version.
|
|
288
|
+
new_key.ver = new_key.depth == 1 ? nil : version
|
|
286
289
|
new_key
|
|
287
290
|
end
|
|
288
291
|
|
|
289
292
|
# get version bytes using serialization format
|
|
290
293
|
def version
|
|
294
|
+
return ver if ver
|
|
291
295
|
return ExtPubkey.version_from_purpose(number) if depth == 1
|
|
292
|
-
|
|
296
|
+
Tapyrus.chain_params.extended_pubkey_version
|
|
293
297
|
end
|
|
294
298
|
|
|
295
299
|
# get key type defined by BIP-178 using version.
|
data/lib/tapyrus/key.rb
CHANGED
|
@@ -320,7 +320,7 @@ module Tapyrus
|
|
|
320
320
|
if low_r && !sig_has_low_r?(sig)
|
|
321
321
|
counter = 1
|
|
322
322
|
until sig_has_low_r?(sig)
|
|
323
|
-
extra_entropy = [counter].pack("
|
|
323
|
+
extra_entropy = [counter].pack("V").bth.ljust(64, "0").htb
|
|
324
324
|
sig = secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: :ecdsa)
|
|
325
325
|
counter += 1
|
|
326
326
|
end
|
data/lib/tapyrus/key_path.rb
CHANGED
|
@@ -3,20 +3,24 @@ module Tapyrus
|
|
|
3
3
|
# key path convert an array of derive number
|
|
4
4
|
# @param [String] path_string
|
|
5
5
|
# @return [Array[Integer]] key path numbers.
|
|
6
|
+
# @raise [ArgumentError] if the path is not a sequence of decimal indexes, each optionally
|
|
7
|
+
# followed by the hardened marker and each below Tapyrus::HARDENED_THRESHOLD.
|
|
6
8
|
def parse_key_path(path_string)
|
|
7
|
-
path_string
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
elements = path_string.split("/")
|
|
10
|
+
raise ArgumentError.new("#{path_string} is invalid format.") unless elements.first == "m"
|
|
11
|
+
elements[1..-1].map do |element|
|
|
12
|
+
hardened = element.end_with?("'")
|
|
13
|
+
# The index is validated before it is converted, since String#to_i stops at the first
|
|
14
|
+
# character which is not a digit and returns what it read so far. An element such as
|
|
15
|
+
# "1'2" would otherwise be taken for the index 1.
|
|
16
|
+
digits = hardened ? element[0..-2] : element
|
|
17
|
+
raise ArgumentError.new("#{path_string} is invalid format.") unless digits =~ /\A[0-9]+\z/
|
|
18
|
+
index = digits.to_i
|
|
19
|
+
# An index of Tapyrus::HARDENED_THRESHOLD or above is the hardened child of a smaller
|
|
20
|
+
# index, so it has no representation of its own in this notation.
|
|
21
|
+
raise ArgumentError.new("#{path_string} is invalid format.") if index >= Tapyrus::HARDENED_THRESHOLD
|
|
22
|
+
hardened ? index + Tapyrus::HARDENED_THRESHOLD : index
|
|
23
|
+
end
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
# key path numbers convert to path string.
|
|
@@ -15,13 +15,13 @@ module Tapyrus
|
|
|
15
15
|
@nonce = nonce
|
|
16
16
|
@short_ids = short_ids
|
|
17
17
|
@prefilled_txn = prefilled_txn
|
|
18
|
-
@siphash_key = Tapyrus.sha256(header.to_payload << [nonce].pack("q
|
|
18
|
+
@siphash_key = Tapyrus.sha256(header.to_payload << [nonce].pack("q<*"))[0...16]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def self.parse_from_payload(payload)
|
|
22
22
|
buf = StringIO.new(payload)
|
|
23
23
|
header = Tapyrus::BlockHeader.parse_from_payload(buf)
|
|
24
|
-
nonce = buf.read(8).unpack("q
|
|
24
|
+
nonce = buf.read(8).unpack("q<*").first
|
|
25
25
|
short_ids_len = Tapyrus.unpack_var_int_from_io(buf)
|
|
26
26
|
short_ids = short_ids_len.times.map { buf.read(6).reverse.bth.to_i(16) }
|
|
27
27
|
prefilled_txn_len = Tapyrus.unpack_var_int_from_io(buf)
|
|
@@ -31,9 +31,9 @@ module Tapyrus
|
|
|
31
31
|
|
|
32
32
|
def to_payload
|
|
33
33
|
p = header.to_payload
|
|
34
|
-
p << [nonce].pack("q
|
|
34
|
+
p << [nonce].pack("q<*")
|
|
35
35
|
p << Tapyrus.pack_var_int(short_ids.size)
|
|
36
|
-
p << short_ids.map { |id| sprintf("%
|
|
36
|
+
p << short_ids.map { |id| sprintf("%012x", id).htb.reverse }.join
|
|
37
37
|
p << Tapyrus.pack_var_int(prefilled_txn.size)
|
|
38
38
|
p << prefilled_txn.map(&:to_payload).join
|
|
39
39
|
p
|
|
@@ -27,7 +27,7 @@ module Tapyrus
|
|
|
27
27
|
# parse inventory payload
|
|
28
28
|
def self.parse_from_payload(payload)
|
|
29
29
|
raise Error, "invalid inventory size." if payload.bytesize != 36
|
|
30
|
-
identifier = payload[0..
|
|
30
|
+
identifier = payload[0..3].unpack("V").first
|
|
31
31
|
hash = payload[4..-1].bth # internal byte order
|
|
32
32
|
new(identifier, hash)
|
|
33
33
|
end
|
|
@@ -35,7 +35,7 @@ module Tapyrus
|
|
|
35
35
|
has_time = buf.size > 26
|
|
36
36
|
addr = new(time: nil)
|
|
37
37
|
addr.time = buf.read(4).unpack("V").first if has_time
|
|
38
|
-
addr.services = buf.read(8).unpack("Q").first
|
|
38
|
+
addr.services = buf.read(8).unpack("Q<").first
|
|
39
39
|
addr.ip_addr = IPAddr.new_ntoh(buf.read(16))
|
|
40
40
|
addr.port = buf.read(2).unpack("n").first
|
|
41
41
|
addr
|
|
@@ -57,7 +57,7 @@ module Tapyrus
|
|
|
57
57
|
p = ""
|
|
58
58
|
p << [time].pack("V") unless skip_time
|
|
59
59
|
addr = ip_addr.ipv4? ? ip_addr.ipv4_mapped : ip_addr
|
|
60
|
-
p << [services].pack("Q") << addr.hton << [port].pack("n")
|
|
60
|
+
p << [services].pack("Q<") << addr.hton << [port].pack("n")
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
end
|
data/lib/tapyrus/message/ping.rb
CHANGED
data/lib/tapyrus/message/pong.rb
CHANGED
|
@@ -21,12 +21,12 @@ module Tapyrus
|
|
|
21
21
|
def self.parse_from_payload(payload)
|
|
22
22
|
buf = StringIO.new(payload)
|
|
23
23
|
mode = buf.read(1).unpack("c").first
|
|
24
|
-
version = buf.read(8).unpack("Q").first
|
|
24
|
+
version = buf.read(8).unpack("Q<").first
|
|
25
25
|
new(mode, version)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def to_payload
|
|
29
|
-
[mode, version].pack("cQ")
|
|
29
|
+
[mode, version].pack("cQ<")
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def high?
|
|
@@ -30,7 +30,7 @@ module Tapyrus
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def self.parse_from_payload(payload)
|
|
33
|
-
version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack("
|
|
33
|
+
version, services, timestamp, local_addr, remote_addr, nonce, rest = payload.unpack("VQ<Q<a26a26Q<a*")
|
|
34
34
|
v = new
|
|
35
35
|
v.version = version
|
|
36
36
|
v.services = services
|
|
@@ -48,10 +48,10 @@ module Tapyrus
|
|
|
48
48
|
|
|
49
49
|
def to_payload
|
|
50
50
|
[
|
|
51
|
-
[version, services, timestamp].pack("
|
|
51
|
+
[version, services, timestamp].pack("VQ<Q<"),
|
|
52
52
|
local_addr.to_payload(true),
|
|
53
53
|
remote_addr.to_payload(true),
|
|
54
|
-
[nonce].pack("Q"),
|
|
54
|
+
[nonce].pack("Q<"),
|
|
55
55
|
pack_var_string(user_agent),
|
|
56
56
|
[start_height].pack("V"),
|
|
57
57
|
pack_boolean(relay)
|
data/lib/tapyrus/mnemonic.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Tapyrus
|
|
|
24
24
|
mnemonic =
|
|
25
25
|
words
|
|
26
26
|
.map do |w|
|
|
27
|
-
index = word_master.index(w.downcase)
|
|
27
|
+
index = word_master.index(normalize(w).downcase)
|
|
28
28
|
raise IndexError, "word not found in words list." unless index
|
|
29
29
|
index.to_s(2).rjust(11, "0")
|
|
30
30
|
end
|
|
@@ -55,8 +55,8 @@ module Tapyrus
|
|
|
55
55
|
def to_seed(mnemonic, passphrase: "")
|
|
56
56
|
to_entropy(mnemonic)
|
|
57
57
|
OpenSSL::PKCS5.pbkdf2_hmac(
|
|
58
|
-
mnemonic.join(" ").downcase,
|
|
59
|
-
"mnemonic" + passphrase,
|
|
58
|
+
normalize(mnemonic.join(" ")).downcase,
|
|
59
|
+
"mnemonic" + normalize(passphrase),
|
|
60
60
|
2048,
|
|
61
61
|
64,
|
|
62
62
|
OpenSSL::Digest::SHA512.new
|
|
@@ -75,7 +75,12 @@ module Tapyrus
|
|
|
75
75
|
|
|
76
76
|
# load word list contents
|
|
77
77
|
def load_words
|
|
78
|
-
File.readlines("#{WORD_DIR}/#{word_list}.txt").map(
|
|
78
|
+
File.readlines("#{WORD_DIR}/#{word_list}.txt", encoding: Encoding::UTF_8).map { |w| normalize(w.strip) }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# BIP-39 requires that mnemonic sentences and passphrases are normalized using NFKD.
|
|
82
|
+
def normalize(str)
|
|
83
|
+
str.unicode_normalize(:nfkd)
|
|
79
84
|
end
|
|
80
85
|
end
|
|
81
86
|
end
|