tapyrus 0.2.7 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.prettierignore +3 -0
- data/.prettierrc.yaml +3 -0
- data/.ruby-version +1 -1
- data/CODE_OF_CONDUCT.md +7 -7
- data/README.md +14 -17
- data/Rakefile +3 -3
- data/lib/openassets/marker_output.rb +0 -4
- data/lib/openassets/payload.rb +4 -10
- data/lib/openassets.rb +0 -2
- data/lib/schnorr/sign_to_contract.rb +51 -0
- data/lib/schnorr/signature.rb +3 -6
- data/lib/schnorr.rb +14 -9
- data/lib/tapyrus/base58.rb +7 -6
- data/lib/tapyrus/bip175.rb +78 -0
- data/lib/tapyrus/block.rb +1 -2
- data/lib/tapyrus/block_header.rb +15 -9
- data/lib/tapyrus/bloom_filter.rb +5 -3
- data/lib/tapyrus/chain_params.rb +1 -4
- data/lib/tapyrus/chainparams/dev.yml +3 -2
- data/lib/tapyrus/chainparams/prod.yml +3 -2
- data/lib/tapyrus/constants.rb +29 -23
- data/lib/tapyrus/errors.rb +1 -3
- data/lib/tapyrus/ext/ecdsa.rb +4 -4
- data/lib/tapyrus/ext/json_parser.rb +1 -4
- data/lib/tapyrus/ext.rb +1 -1
- data/lib/tapyrus/ext_key.rb +44 -32
- data/lib/tapyrus/key.rb +31 -35
- data/lib/tapyrus/key_path.rb +15 -12
- data/lib/tapyrus/logger.rb +20 -16
- data/lib/tapyrus/merkle_tree.rb +22 -20
- data/lib/tapyrus/message/addr.rb +1 -7
- data/lib/tapyrus/message/base.rb +0 -3
- data/lib/tapyrus/message/block.rb +2 -9
- data/lib/tapyrus/message/block_transaction_request.rb +3 -6
- data/lib/tapyrus/message/block_transactions.rb +2 -6
- data/lib/tapyrus/message/block_txn.rb +0 -4
- data/lib/tapyrus/message/cmpct_block.rb +1 -7
- data/lib/tapyrus/message/error.rb +1 -4
- data/lib/tapyrus/message/fee_filter.rb +1 -4
- data/lib/tapyrus/message/filter_add.rb +0 -4
- data/lib/tapyrus/message/filter_clear.rb +0 -4
- data/lib/tapyrus/message/filter_load.rb +2 -5
- data/lib/tapyrus/message/get_addr.rb +0 -4
- data/lib/tapyrus/message/get_block_txn.rb +0 -4
- data/lib/tapyrus/message/get_blocks.rb +0 -3
- data/lib/tapyrus/message/get_data.rb +1 -4
- data/lib/tapyrus/message/get_headers.rb +1 -3
- data/lib/tapyrus/message/header_and_short_ids.rb +3 -9
- data/lib/tapyrus/message/headers.rb +0 -4
- data/lib/tapyrus/message/headers_parser.rb +3 -8
- data/lib/tapyrus/message/inv.rb +1 -4
- data/lib/tapyrus/message/inventories_parser.rb +2 -7
- data/lib/tapyrus/message/inventory.rb +12 -5
- data/lib/tapyrus/message/mem_pool.rb +0 -4
- data/lib/tapyrus/message/merkle_block.rb +4 -9
- data/lib/tapyrus/message/network_addr.rb +7 -6
- data/lib/tapyrus/message/not_found.rb +0 -3
- data/lib/tapyrus/message/ping.rb +0 -3
- data/lib/tapyrus/message/pong.rb +0 -3
- data/lib/tapyrus/message/prefilled_tx.rb +0 -4
- data/lib/tapyrus/message/reject.rb +0 -3
- data/lib/tapyrus/message/send_cmpct.rb +1 -3
- data/lib/tapyrus/message/send_headers.rb +0 -3
- data/lib/tapyrus/message/tx.rb +0 -4
- data/lib/tapyrus/message/ver_ack.rb +1 -5
- data/lib/tapyrus/message/version.rb +2 -5
- data/lib/tapyrus/message.rb +14 -16
- data/lib/tapyrus/mnemonic.rb +17 -15
- data/lib/tapyrus/network/connection.rb +0 -3
- data/lib/tapyrus/network/message_handler.rb +61 -60
- data/lib/tapyrus/network/peer.rb +13 -12
- data/lib/tapyrus/network/peer_discovery.rb +10 -9
- data/lib/tapyrus/network/pool.rb +12 -12
- data/lib/tapyrus/network.rb +0 -2
- data/lib/tapyrus/node/cli.rb +12 -14
- data/lib/tapyrus/node/configuration.rb +1 -3
- data/lib/tapyrus/node/spv.rb +2 -3
- data/lib/tapyrus/node.rb +1 -1
- data/lib/tapyrus/opcodes.rb +9 -7
- data/lib/tapyrus/out_point.rb +5 -5
- data/lib/tapyrus/rpc/http_server.rb +21 -22
- data/lib/tapyrus/rpc/request_handler.rb +16 -21
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +67 -25
- data/lib/tapyrus/rpc.rb +1 -0
- data/lib/tapyrus/script/color.rb +10 -0
- data/lib/tapyrus/script/multisig.rb +13 -12
- data/lib/tapyrus/script/script.rb +93 -88
- data/lib/tapyrus/script/script_error.rb +1 -4
- data/lib/tapyrus/script/script_interpreter.rb +439 -399
- data/lib/tapyrus/script/tx_checker.rb +20 -10
- data/lib/tapyrus/secp256k1/native.rb +14 -15
- data/lib/tapyrus/secp256k1/rfc6979.rb +7 -4
- data/lib/tapyrus/secp256k1/ruby.rb +10 -12
- data/lib/tapyrus/secp256k1.rb +0 -4
- data/lib/tapyrus/slip39/share.rb +41 -29
- data/lib/tapyrus/slip39/sss.rb +92 -49
- data/lib/tapyrus/slip39.rb +20 -5
- data/lib/tapyrus/store/chain_entry.rb +0 -4
- data/lib/tapyrus/store/db/level_db.rb +5 -9
- data/lib/tapyrus/store/db.rb +0 -2
- data/lib/tapyrus/store/spv_chain.rb +11 -17
- data/lib/tapyrus/store.rb +1 -3
- data/lib/tapyrus/tx.rb +45 -37
- data/lib/tapyrus/tx_builder.rb +160 -0
- data/lib/tapyrus/tx_in.rb +1 -6
- data/lib/tapyrus/tx_out.rb +2 -7
- data/lib/tapyrus/util.rb +7 -9
- data/lib/tapyrus/validation.rb +12 -11
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +22 -18
- data/lib/tapyrus/wallet/base.rb +12 -9
- data/lib/tapyrus/wallet/db.rb +6 -9
- data/lib/tapyrus/wallet/master_key.rb +2 -4
- data/lib/tapyrus.rb +7 -22
- data/tapyrusrb.gemspec +13 -14
- metadata +26 -7
- data/.travis.yml +0 -14
data/lib/tapyrus/network/peer.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
module Tapyrus
|
2
2
|
module Network
|
3
|
-
|
4
3
|
# remote peer class.
|
5
4
|
class Peer
|
6
|
-
|
7
5
|
# Interval for pinging peers.
|
8
6
|
PING_INTERVAL = 2 * 60
|
9
7
|
|
@@ -22,13 +20,16 @@ module Tapyrus
|
|
22
20
|
attr_accessor :outbound # TODO need implements to accept inbound connection
|
23
21
|
attr_accessor :best_hash
|
24
22
|
attr_accessor :best_height
|
23
|
+
|
25
24
|
# remote peer info
|
26
25
|
attr_reader :host
|
27
26
|
attr_reader :port
|
27
|
+
|
28
28
|
# remote peer connection
|
29
29
|
attr_accessor :conn
|
30
30
|
attr_accessor :connected
|
31
31
|
attr_accessor :primary
|
32
|
+
|
32
33
|
# parent pool
|
33
34
|
attr_reader :pool
|
34
35
|
attr_reader :chain
|
@@ -51,7 +52,8 @@ module Tapyrus
|
|
51
52
|
@relay = configuration.conf[:relay]
|
52
53
|
current_height = @chain.latest_block.height
|
53
54
|
remote_addr = Tapyrus::Message::NetworkAddr.new(ip: host, port: port, time: nil)
|
54
|
-
@local_version =
|
55
|
+
@local_version =
|
56
|
+
Tapyrus::Message::Version.new(remote_addr: remote_addr, start_height: current_height, relay: @relay)
|
55
57
|
end
|
56
58
|
|
57
59
|
def connect
|
@@ -84,16 +86,17 @@ module Tapyrus
|
|
84
86
|
def post_handshake
|
85
87
|
@connected = true
|
86
88
|
pool.handle_new_peer(self)
|
89
|
+
|
87
90
|
# require remote peer to use headers message instead fo inv message.
|
88
91
|
conn.send_message(Tapyrus::Message::SendHeaders.new)
|
89
|
-
EM.add_periodic_timer(PING_INTERVAL) {send_ping}
|
92
|
+
EM.add_periodic_timer(PING_INTERVAL) { send_ping }
|
90
93
|
end
|
91
94
|
|
92
95
|
# start block header download
|
93
96
|
def start_block_header_download
|
94
97
|
logger.info("[#{addr}] start block header download.")
|
95
|
-
get_headers =
|
96
|
-
|
98
|
+
get_headers =
|
99
|
+
Tapyrus::Message::GetHeaders.new(Tapyrus.chain_params.protocol_version, [chain.latest_block.block_hash])
|
97
100
|
conn.send_message(get_headers)
|
98
101
|
end
|
99
102
|
|
@@ -129,7 +132,7 @@ module Tapyrus
|
|
129
132
|
@best_height = entry.height
|
130
133
|
end
|
131
134
|
pool.changed
|
132
|
-
pool.notify_observers(:header, {hash: @best_hash, height: @best_height})
|
135
|
+
pool.notify_observers(:header, { hash: @best_hash, height: @best_height })
|
133
136
|
start_block_header_download if headers.headers.size > 0 # next header download
|
134
137
|
end
|
135
138
|
|
@@ -156,14 +159,13 @@ module Tapyrus
|
|
156
159
|
|
157
160
|
# send +addr+ message to remote peer
|
158
161
|
def send_addrs
|
159
|
-
addrs = pool.peers.select{|p|p != self}.map(&:to_network_addr)
|
162
|
+
addrs = pool.peers.select { |p| p != self }.map(&:to_network_addr)
|
160
163
|
conn.send_message(Tapyrus::Message::Addr.new(addrs))
|
161
164
|
end
|
162
165
|
|
163
166
|
# handle block inv message.
|
164
167
|
def handle_block_inv(hashes)
|
165
|
-
getdata = Tapyrus::Message::GetData.new(
|
166
|
-
hashes.map{|h|Tapyrus::Message::Inventory.new(block_type, h)})
|
168
|
+
getdata = Tapyrus::Message::GetData.new(hashes.map { |h| Tapyrus::Message::Inventory.new(block_type, h) })
|
167
169
|
conn.send_message(getdata)
|
168
170
|
end
|
169
171
|
|
@@ -188,8 +190,7 @@ module Tapyrus
|
|
188
190
|
|
189
191
|
# send filterload message.
|
190
192
|
def send_filter_load(bloom)
|
191
|
-
filter_load = Tapyrus::Message::FilterLoad.new(
|
192
|
-
bloom, Tapyrus::Message::FilterLoad::BLOOM_UPDATE_ALL)
|
193
|
+
filter_load = Tapyrus::Message::FilterLoad.new(bloom, Tapyrus::Message::FilterLoad::BLOOM_UPDATE_ALL)
|
193
194
|
conn.send_message(filter_load)
|
194
195
|
end
|
195
196
|
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Tapyrus
|
2
2
|
module Network
|
3
|
-
|
4
3
|
class PeerDiscovery
|
5
|
-
|
6
4
|
attr_reader :logger, :configuration
|
7
5
|
|
8
6
|
def initialize(configuration)
|
@@ -28,14 +26,17 @@ module Tapyrus
|
|
28
26
|
|
29
27
|
def find_from_dns_seeds
|
30
28
|
logger.debug 'discover peer address from DNS seeds.'
|
31
|
-
dns_seeds
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
dns_seeds
|
30
|
+
.map do |seed|
|
31
|
+
begin
|
32
|
+
Socket.getaddrinfo(seed, Tapyrus.chain_params.default_port).map { |a| a[2] }.uniq
|
33
|
+
rescue SocketError => e
|
34
|
+
logger.error "SocketError occurred when load DNS seed: #{seed}, error: #{e.message}"
|
35
|
+
nil
|
36
|
+
end
|
37
37
|
end
|
38
|
-
|
38
|
+
.flatten
|
39
|
+
.compact
|
39
40
|
end
|
40
41
|
end
|
41
42
|
end
|
data/lib/tapyrus/network/pool.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
module Network
|
4
|
-
|
5
3
|
# Time between pings automatically sent out for latency probing and keepalive (in seconds).
|
6
4
|
PING_INTERVAL = 2 * 60
|
5
|
+
|
7
6
|
# Time after which to disconnect, after waiting for a ping response (or inactivity).
|
8
7
|
TIMEOUT_INTERVAL = 20 * 60
|
8
|
+
|
9
9
|
# Maximum number of automatic outgoing nodes
|
10
10
|
MAX_OUTBOUND_CONNECTIONS = 4
|
11
11
|
|
@@ -112,23 +112,23 @@ module Tapyrus
|
|
112
112
|
# allocate new peer id
|
113
113
|
def allocate_peer_id
|
114
114
|
id = 0
|
115
|
-
until peers.empty? || peers.find{|p|p.id == id}.nil?
|
116
|
-
id += 1
|
117
|
-
end
|
115
|
+
id += 1 until peers.empty? || peers.find { |p| p.id == id }.nil?
|
118
116
|
id
|
119
117
|
end
|
120
118
|
|
121
119
|
def connect(addr_list)
|
122
120
|
port = Tapyrus.chain_params.default_port
|
123
121
|
|
124
|
-
EM::Iterator
|
125
|
-
|
126
|
-
|
127
|
-
pending_peers
|
128
|
-
|
129
|
-
|
122
|
+
EM::Iterator
|
123
|
+
.new(addr_list, Tapyrus::PARALLEL_THREAD)
|
124
|
+
.each do |ip, iter|
|
125
|
+
if pending_peers.size + peers.size < MAX_OUTBOUND_CONNECTIONS
|
126
|
+
peer = Peer.new(ip, port, self, @configuration)
|
127
|
+
pending_peers << peer
|
128
|
+
peer.connect
|
129
|
+
iter.next
|
130
|
+
end
|
130
131
|
end
|
131
|
-
end
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|
data/lib/tapyrus/network.rb
CHANGED
@@ -2,12 +2,10 @@ require 'eventmachine'
|
|
2
2
|
|
3
3
|
module Tapyrus
|
4
4
|
module Network
|
5
|
-
|
6
5
|
autoload :MessageHandler, 'tapyrus/network/message_handler'
|
7
6
|
autoload :Connection, 'tapyrus/network/connection'
|
8
7
|
autoload :Pool, 'tapyrus/network/pool'
|
9
8
|
autoload :Peer, 'tapyrus/network/peer'
|
10
9
|
autoload :PeerDiscovery, 'tapyrus/network/peer_discovery'
|
11
|
-
|
12
10
|
end
|
13
11
|
end
|
data/lib/tapyrus/node/cli.rb
CHANGED
@@ -4,9 +4,7 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Tapyrus
|
6
6
|
module Node
|
7
|
-
|
8
7
|
class CLI < Thor
|
9
|
-
|
10
8
|
class_option :network, aliases: '-n', default: :prod
|
11
9
|
|
12
10
|
desc 'getblockchaininfo', 'Returns an object containing various state info regarding blockchain processing.'
|
@@ -19,7 +17,8 @@ module Tapyrus
|
|
19
17
|
request('stop')
|
20
18
|
end
|
21
19
|
|
22
|
-
desc 'getblockheader "hash" ( verbose )',
|
20
|
+
desc 'getblockheader "hash" ( verbose )',
|
21
|
+
'If verbose is false, returns a string that is serialized, hex-encoded data for blockheader "hash". If verbose is true, returns an Object with information about blockheader <hash>.'
|
23
22
|
def getblockheader(hash, verbose = true)
|
24
23
|
verbose = verbose.is_a?(String) ? (verbose == 'true') : verbose
|
25
24
|
request('getblockheader', hash, verbose)
|
@@ -30,7 +29,8 @@ module Tapyrus
|
|
30
29
|
request('getpeerinfo')
|
31
30
|
end
|
32
31
|
|
33
|
-
desc 'decoderawtransaction "hexstring"',
|
32
|
+
desc 'decoderawtransaction "hexstring"',
|
33
|
+
'Return a JSON object representing the serialized, hex-encoded transaction.'
|
34
34
|
def decoderawtransaction(hexstring)
|
35
35
|
request('decoderawtransaction', hexstring)
|
36
36
|
end
|
@@ -47,12 +47,14 @@ module Tapyrus
|
|
47
47
|
request('sendrawtransaction', hex_tx)
|
48
48
|
end
|
49
49
|
|
50
|
-
desc 'createwallet "wallet_id"',
|
50
|
+
desc 'createwallet "wallet_id"',
|
51
|
+
'Create new HD wallet. It returns an error if an existing wallet_id is specified. '
|
51
52
|
def createwallet(wallet_id)
|
52
53
|
request('createwallet', wallet_id)
|
53
54
|
end
|
54
55
|
|
55
|
-
desc 'listwallets',
|
56
|
+
desc 'listwallets',
|
57
|
+
'Returns a list of currently loaded wallets. For full information on the wallet, use "getwalletinfo"'
|
56
58
|
def listwallets
|
57
59
|
request('listwallets')
|
58
60
|
end
|
@@ -67,7 +69,8 @@ module Tapyrus
|
|
67
69
|
request('listaccounts')
|
68
70
|
end
|
69
71
|
|
70
|
-
desc 'encryptwallet "passphrase"',
|
72
|
+
desc 'encryptwallet "passphrase"',
|
73
|
+
'Encrypts the wallet with "passphrase". This is for first time encryption.After this, any calls that interact with private keys such as sending or signing will require the passphrase to be set prior the making these calls.'
|
71
74
|
def encryptwallet(passhphrase)
|
72
75
|
request('encryptwallet', passhphrase)
|
73
76
|
end
|
@@ -86,15 +89,11 @@ module Tapyrus
|
|
86
89
|
end
|
87
90
|
|
88
91
|
def request(command, *params)
|
89
|
-
data = {
|
90
|
-
:method => command,
|
91
|
-
:params => params,
|
92
|
-
:id => 'jsonrpc'
|
93
|
-
}
|
92
|
+
data = { method: command, params: params, id: 'jsonrpc' }
|
94
93
|
begin
|
95
94
|
uri = URI.parse(config.server_url)
|
96
95
|
http = Net::HTTP.new(uri.hostname, uri.port)
|
97
|
-
http.use_ssl = uri.scheme ===
|
96
|
+
http.use_ssl = uri.scheme === 'https'
|
98
97
|
request = Net::HTTP::Post.new('/')
|
99
98
|
request.content_type = 'application/json'
|
100
99
|
request.body = data.to_json
|
@@ -110,7 +109,6 @@ module Tapyrus
|
|
110
109
|
puts e.message
|
111
110
|
end
|
112
111
|
end
|
113
|
-
|
114
112
|
end
|
115
113
|
end
|
116
114
|
end
|
@@ -3,7 +3,6 @@ require 'iniparse'
|
|
3
3
|
module Tapyrus
|
4
4
|
module Node
|
5
5
|
class Configuration
|
6
|
-
|
7
6
|
attr_reader :conf
|
8
7
|
|
9
8
|
def initialize(opts = {})
|
@@ -14,7 +13,7 @@ module Tapyrus
|
|
14
13
|
|
15
14
|
begin
|
16
15
|
ini_file = IniParse.parse(File.read("#{Tapyrus.base_dir}/tapyrusrb.conf"))
|
17
|
-
@conf = Hash[
|
16
|
+
@conf = Hash[ini_file.to_h['__anonymous__'].map { |k, v| [k.to_sym, v] }]
|
18
17
|
rescue => e
|
19
18
|
@conf = {}
|
20
19
|
end
|
@@ -32,7 +31,6 @@ module Tapyrus
|
|
32
31
|
def server_url
|
33
32
|
"http://#{host}:#{port}"
|
34
33
|
end
|
35
|
-
|
36
34
|
end
|
37
35
|
end
|
38
36
|
end
|
data/lib/tapyrus/node/spv.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
module Tapyrus
|
2
2
|
module Node
|
3
|
-
|
4
3
|
# SPV class
|
5
4
|
class SPV
|
6
|
-
|
7
5
|
attr_reader :chain
|
8
6
|
attr_reader :pool
|
9
7
|
attr_reader :logger
|
@@ -20,6 +18,7 @@ module Tapyrus
|
|
20
18
|
@logger = Tapyrus::Logger.create(:debug)
|
21
19
|
@running = false
|
22
20
|
@wallet = Tapyrus::Wallet::Base.current_wallet
|
21
|
+
|
23
22
|
# TODO : optimize bloom filter parameters
|
24
23
|
setup_filter
|
25
24
|
end
|
@@ -72,7 +71,7 @@ module Tapyrus
|
|
72
71
|
|
73
72
|
def setup_filter
|
74
73
|
@bloom = Tapyrus::BloomFilter.create_filter(512, 0.01)
|
75
|
-
wallet.watch_targets.each{|t|bloom.add(t.htb)} if wallet
|
74
|
+
wallet.watch_targets.each { |t| bloom.add(t.htb) } if wallet
|
76
75
|
end
|
77
76
|
end
|
78
77
|
end
|
data/lib/tapyrus/node.rb
CHANGED
data/lib/tapyrus/opcodes.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
# https://bitcoin.org/en/developer-reference#opcodes
|
4
3
|
module Opcodes
|
5
|
-
|
6
4
|
module_function
|
7
5
|
|
8
6
|
# https://en.bitcoin.it/wiki/Script#Constants
|
@@ -111,7 +109,7 @@ module Tapyrus
|
|
111
109
|
OP_HASH256 = 0xaa
|
112
110
|
OP_CODESEPARATOR = 0xab
|
113
111
|
OP_CHECKSIG = 0xac
|
114
|
-
OP_CHECKSIGVERIFY= 0xad
|
112
|
+
OP_CHECKSIGVERIFY = 0xad
|
115
113
|
OP_CHECKMULTISIG = 0xae
|
116
114
|
OP_CHECKMULTISIGVERIFY = 0xaf
|
117
115
|
|
@@ -124,7 +122,7 @@ module Tapyrus
|
|
124
122
|
OP_VER = 0x62
|
125
123
|
OP_VERIF = 0x65
|
126
124
|
OP_VERNOTIF = 0x66
|
127
|
-
OP_RESERVED1= 0x89
|
125
|
+
OP_RESERVED1 = 0x89
|
128
126
|
OP_RESERVED2 = 0x8a
|
129
127
|
|
130
128
|
OP_NOP1 = 0xb0
|
@@ -147,7 +145,12 @@ module Tapyrus
|
|
147
145
|
OP_COLOR = 0xbc
|
148
146
|
|
149
147
|
DUPLICATE_KEY = [:OP_NOP2, :OP_NOP3]
|
150
|
-
OPCODES_MAP =
|
148
|
+
OPCODES_MAP =
|
149
|
+
Hash[
|
150
|
+
*(constants.grep(/^OP_/) - [:OP_NOP2, :OP_NOP3, :OP_CHECKLOCKTIMEVERIFY, :OP_CHECKSEQUENCEVERIFY]).map do |c|
|
151
|
+
[const_get(c), c.to_s]
|
152
|
+
end.flatten
|
153
|
+
]
|
151
154
|
NAME_MAP = Hash[*constants.grep(/^OP_/).map { |c| [c.to_s, const_get(c)] }.flatten]
|
152
155
|
|
153
156
|
def opcode_to_name(opcode)
|
@@ -178,6 +181,5 @@ module Tapyrus
|
|
178
181
|
return opcode - (OP_1 - 1) if opcode >= OP_1 && opcode <= OP_16
|
179
182
|
nil
|
180
183
|
end
|
181
|
-
|
182
184
|
end
|
183
|
-
end
|
185
|
+
end
|
data/lib/tapyrus/out_point.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
module Tapyrus
|
2
|
-
|
3
2
|
# outpoint class
|
4
3
|
class OutPoint
|
5
|
-
|
6
4
|
COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
|
7
|
-
COINBASE_INDEX =
|
5
|
+
COINBASE_INDEX = 4_294_967_295
|
8
6
|
|
9
7
|
attr_reader :tx_hash
|
10
8
|
attr_reader :index
|
@@ -34,11 +32,13 @@ module Tapyrus
|
|
34
32
|
index >= 0 && (!coinbase? && tx_hash != COINBASE_HASH)
|
35
33
|
end
|
36
34
|
|
35
|
+
def ==(other)
|
36
|
+
to_payload == other&.to_payload
|
37
|
+
end
|
38
|
+
|
37
39
|
# convert hash to txid
|
38
40
|
def txid
|
39
41
|
tx_hash.rhex
|
40
42
|
end
|
41
|
-
|
42
43
|
end
|
43
|
-
|
44
44
|
end
|
@@ -3,7 +3,6 @@ require 'json'
|
|
3
3
|
|
4
4
|
module Tapyrus
|
5
5
|
module RPC
|
6
|
-
|
7
6
|
# Tapyrusrb RPC server.
|
8
7
|
class HttpServer < EM::Connection
|
9
8
|
include EM::HttpServer
|
@@ -28,27 +27,29 @@ module Tapyrus
|
|
28
27
|
|
29
28
|
# process http request.
|
30
29
|
def process_http_request
|
31
|
-
operation =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
e
|
30
|
+
operation =
|
31
|
+
proc do
|
32
|
+
command, args = parse_json_params
|
33
|
+
logger.debug("process http request. command = #{command}")
|
34
|
+
begin
|
35
|
+
send(command, *args).to_json
|
36
|
+
rescue Exception => e
|
37
|
+
e
|
38
|
+
end
|
38
39
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
callback =
|
41
|
+
proc do |result|
|
42
|
+
response = EM::DelegatedHttpResponse.new(self)
|
43
|
+
if result.is_a?(Exception)
|
44
|
+
response.status = 500
|
45
|
+
response.content = result.message
|
46
|
+
else
|
47
|
+
response.status = 200
|
48
|
+
response.content = result
|
49
|
+
end
|
50
|
+
response.content_type 'application/json'
|
51
|
+
response.send_response
|
48
52
|
end
|
49
|
-
response.content_type 'application/json'
|
50
|
-
response.send_response
|
51
|
-
}
|
52
53
|
EM.defer(operation, callback)
|
53
54
|
end
|
54
55
|
|
@@ -58,8 +59,6 @@ module Tapyrus
|
|
58
59
|
params = JSON.parse(@http_post_content)
|
59
60
|
[params['method'], params['params']]
|
60
61
|
end
|
61
|
-
|
62
62
|
end
|
63
|
-
|
64
63
|
end
|
65
64
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
module Tapyrus
|
2
2
|
module RPC
|
3
|
-
|
4
3
|
# RPC server's request handler.
|
5
4
|
module RequestHandler
|
6
|
-
|
7
5
|
# Returns an object containing various state info regarding blockchain processing.
|
8
6
|
def getblockchaininfo
|
9
7
|
h = {}
|
@@ -28,19 +26,19 @@ module Tapyrus
|
|
28
26
|
raise ArgumentError.new('Block not found') unless entry
|
29
27
|
if verbose
|
30
28
|
{
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
29
|
+
hash: block_id,
|
30
|
+
height: entry.height,
|
31
|
+
features: entry.header.features,
|
32
|
+
featuresHex: entry.header.features.to_even_length_hex.ljust(8, '0'),
|
33
|
+
merkleroot: entry.header.merkle_root.rhex,
|
34
|
+
immutablemerkleroot: entry.header.im_merkle_root.rhex,
|
35
|
+
time: entry.header.time,
|
36
|
+
mediantime: node.chain.mtp(block_hash),
|
37
|
+
xfield_type: entry.header.x_field_type,
|
38
|
+
xfield: entry.header.x_field,
|
39
|
+
proof: entry.header.proof,
|
40
|
+
previousblockhash: entry.prev_hash.rhex,
|
41
|
+
nextblockhash: node.chain.next_hash(block_hash).rhex
|
44
42
|
}
|
45
43
|
else
|
46
44
|
entry.header.to_hex
|
@@ -77,6 +75,7 @@ module Tapyrus
|
|
77
75
|
# broadcast transaction
|
78
76
|
def sendrawtransaction(hex_tx)
|
79
77
|
tx = Tapyrus::Tx.parse_from_payload(hex_tx.htb)
|
78
|
+
|
80
79
|
# TODO check wether tx is valid
|
81
80
|
node.broadcast(tx)
|
82
81
|
tx.txid
|
@@ -110,7 +109,7 @@ module Tapyrus
|
|
110
109
|
def createwallet(wallet_id = 1, wallet_path_prefix = Tapyrus::Wallet::Base.default_path_prefix)
|
111
110
|
wallet = Tapyrus::Wallet::Base.create(wallet_id, wallet_path_prefix)
|
112
111
|
node.wallet = wallet unless node.wallet
|
113
|
-
{wallet_id: wallet.wallet_id, mnemonic: wallet.master_key.mnemonic}
|
112
|
+
{ wallet_id: wallet.wallet_id, mnemonic: wallet.master_key.mnemonic }
|
114
113
|
end
|
115
114
|
|
116
115
|
# get wallet list.
|
@@ -127,9 +126,7 @@ module Tapyrus
|
|
127
126
|
def listaccounts
|
128
127
|
return {} unless node.wallet
|
129
128
|
accounts = {}
|
130
|
-
node.wallet.accounts.each
|
131
|
-
accounts[a.name] = node.wallet.get_balance(a)
|
132
|
-
end
|
129
|
+
node.wallet.accounts.each { |a| accounts[a.name] = node.wallet.get_balance(a) }
|
133
130
|
accounts
|
134
131
|
end
|
135
132
|
|
@@ -144,8 +141,6 @@ module Tapyrus
|
|
144
141
|
def getnewaddress(account_name)
|
145
142
|
node.wallet.generate_new_address(account_name)
|
146
143
|
end
|
147
|
-
|
148
144
|
end
|
149
|
-
|
150
145
|
end
|
151
146
|
end
|
@@ -3,6 +3,40 @@ require 'json/pure'
|
|
3
3
|
|
4
4
|
module Tapyrus
|
5
5
|
module RPC
|
6
|
+
# Throw when happened anything http's error with connect to server.
|
7
|
+
#
|
8
|
+
# Almost case this exception happened from 401 Unauthorized or 500 Internal Server Error.
|
9
|
+
# And also, throw by cause of other http's errors.
|
10
|
+
#
|
11
|
+
# You can pull RPC error message when happened 500 Internal Server Error, like below:
|
12
|
+
#
|
13
|
+
# rescue Tapyrus::RPC::Error => ex
|
14
|
+
# if ex.message.response_code == 500
|
15
|
+
# puts ex.message[:rpc_error]
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
class Error < StandardError
|
19
|
+
attr_reader :response_code, :response_msg, :rpc_error
|
20
|
+
|
21
|
+
def initialize(response_code, response_msg, rpc_error)
|
22
|
+
@response_code = response_code
|
23
|
+
@response_msg = response_msg
|
24
|
+
@rpc_error = rpc_error
|
25
|
+
end
|
26
|
+
|
27
|
+
def message
|
28
|
+
@message ||=
|
29
|
+
begin
|
30
|
+
m = { response_code: response_code, response_msg: response_msg }
|
31
|
+
m.merge!(rpc_error: rpc_error) if rpc_error
|
32
|
+
m
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
message.to_s
|
38
|
+
end
|
39
|
+
end
|
6
40
|
|
7
41
|
# Client implementation for RPC to Tapyrus Core.
|
8
42
|
#
|
@@ -16,25 +50,21 @@ module Tapyrus
|
|
16
50
|
# client.getblockchaininfo
|
17
51
|
#
|
18
52
|
class TapyrusCoreClient
|
19
|
-
|
20
53
|
attr_reader :config
|
21
54
|
|
22
55
|
# @param [Hash] config a configuration required to connect to Bitcoin Core.
|
23
56
|
def initialize(config)
|
24
57
|
@config = config
|
25
58
|
|
26
|
-
commands =
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
TapyrusCoreClient.class_eval do
|
33
|
-
commands.each do |command|
|
34
|
-
define_method(command) do |*params|
|
35
|
-
request(command, *params)
|
59
|
+
commands =
|
60
|
+
request(:help)
|
61
|
+
.split("\n")
|
62
|
+
.inject([]) do |memo_ary, line|
|
63
|
+
memo_ary << line.split(' ').first.to_sym if !line.empty? && !line.start_with?('==')
|
64
|
+
memo_ary
|
36
65
|
end
|
37
|
-
|
66
|
+
TapyrusCoreClient.class_eval do
|
67
|
+
commands.each { |command| define_method(command) { |*params| request(command, *params) } }
|
38
68
|
end
|
39
69
|
end
|
40
70
|
|
@@ -42,33 +72,45 @@ module Tapyrus
|
|
42
72
|
|
43
73
|
def server_url
|
44
74
|
url = "#{config[:schema]}://#{config[:user]}:#{config[:password]}@#{config[:host]}:#{config[:port]}"
|
45
|
-
if !config[:wallet].nil? && !config[:wallet].empty?
|
46
|
-
url += "/wallet/#{config[:wallet]}"
|
47
|
-
end
|
75
|
+
url += "/wallet/#{config[:wallet]}" if !config[:wallet].nil? && !config[:wallet].empty?
|
48
76
|
url
|
49
77
|
end
|
50
78
|
|
51
79
|
def request(command, *params)
|
52
|
-
data = {
|
53
|
-
:method => command,
|
54
|
-
:params => params,
|
55
|
-
:id => 'jsonrpc'
|
56
|
-
}
|
80
|
+
data = { method: command, params: params, id: 'jsonrpc' }
|
57
81
|
uri = URI.parse(server_url)
|
58
82
|
http = Net::HTTP.new(uri.hostname, uri.port)
|
59
|
-
http.use_ssl = uri.scheme ===
|
83
|
+
http.use_ssl = uri.scheme === 'https'
|
60
84
|
request = Net::HTTP::Post.new(uri.path.empty? ? '/' : uri.path)
|
61
85
|
request.basic_auth(uri.user, uri.password)
|
62
86
|
request.content_type = 'application/json'
|
63
87
|
request.body = data.to_json
|
64
88
|
response = http.request(request)
|
65
|
-
|
66
|
-
response = Tapyrus::
|
67
|
-
raise response['error'].to_json if response['error']
|
89
|
+
raise error!(response) unless response.is_a? Net::HTTPOK
|
90
|
+
response = Tapyrus::RPC.response_body2json(response.body)
|
68
91
|
response['result']
|
69
92
|
end
|
70
93
|
|
94
|
+
def error!(response)
|
95
|
+
rpc_error =
|
96
|
+
begin
|
97
|
+
Tapyrus::RPC.response_body2json(response.body)['error']
|
98
|
+
rescue JSON::ParserError => _
|
99
|
+
# if RPC server don't send error message.
|
100
|
+
end
|
101
|
+
|
102
|
+
raise Error.new(response.code, response.msg, rpc_error)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def response_body2json(body)
|
107
|
+
Tapyrus::Ext::JsonParser.new(
|
108
|
+
body.gsub(/\\u([\da-fA-F]{4})/) do
|
109
|
+
[$1].pack('H*').unpack('n*').pack('U*').encode('ISO-8859-1').force_encoding('UTF-8')
|
110
|
+
end
|
111
|
+
).parse
|
71
112
|
end
|
72
113
|
|
114
|
+
module_function :response_body2json
|
73
115
|
end
|
74
|
-
end
|
116
|
+
end
|