bitcoinrb 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bitcoinrb.gemspec +1 -0
- data/lib/bitcoin/connection.rb +7 -5
- data/lib/bitcoin/constants.rb +157 -0
- data/lib/bitcoin/key.rb +15 -3
- data/lib/bitcoin/message/base.rb +0 -12
- data/lib/bitcoin/message/handler.rb +27 -14
- data/lib/bitcoin/message/version.rb +1 -1
- data/lib/bitcoin/message.rb +10 -1
- data/lib/bitcoin/opcodes.rb +2 -2
- data/lib/bitcoin/out_point.rb +5 -1
- data/lib/bitcoin/script/script.rb +117 -38
- data/lib/bitcoin/script/script_error.rb +0 -61
- data/lib/bitcoin/script/script_interpreter.rb +164 -154
- data/lib/bitcoin/script/tx_checker.rb +48 -12
- data/lib/bitcoin/secp256k1/native.rb +139 -5
- data/lib/bitcoin/secp256k1/ruby.rb +2 -13
- data/lib/bitcoin/tx.rb +115 -18
- data/lib/bitcoin/tx_in.rb +23 -4
- data/lib/bitcoin/tx_out.rb +1 -1
- data/lib/bitcoin/validation.rb +93 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin.rb +31 -2
- metadata +19 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a527d6ef43eda02e2ef56d64edb5da7dfa04a956
|
4
|
+
data.tar.gz: b1a7902b2be175f6f771ca3a418cbf869ab9dc7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec03a93f28a17efdd812eaf1dd13e3a3f5ad947393bd8e29e6040116558dd9bbfc193419cc1a69bd623b36c4bb141e5957010ec989fca9301d851b461a78f79a
|
7
|
+
data.tar.gz: bb41bbb4b43700e9a587e2e8d157a1ab7be358a14b9bd44ab349fdc19275c124e407ea9093f68b8cb218d61c0800efc348c8b4fbc202884ecb8e0ab0c3baa347
|
data/bitcoinrb.gemspec
CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_runtime_dependency 'bech32', '~> 1.0.1'
|
26
26
|
spec.add_runtime_dependency 'daemon-spawn'
|
27
27
|
spec.add_runtime_dependency 'thor'
|
28
|
+
spec.add_runtime_dependency 'ffi'
|
28
29
|
|
29
30
|
spec.add_development_dependency "bundler", "~> 1.11"
|
30
31
|
spec.add_development_dependency "rake", "~> 10.0"
|
data/lib/bitcoin/connection.rb
CHANGED
@@ -29,7 +29,7 @@ module Bitcoin
|
|
29
29
|
|
30
30
|
# handle receiving data from remote node.
|
31
31
|
def receive_data(data)
|
32
|
-
logger.info "receive data from #{remote_node}
|
32
|
+
logger.info "receive data from #{remote_node}"
|
33
33
|
handler.handle(data)
|
34
34
|
end
|
35
35
|
|
@@ -43,8 +43,11 @@ module Bitcoin
|
|
43
43
|
def handshake_done
|
44
44
|
logger.info 'handshake finished.'
|
45
45
|
@connected = true
|
46
|
-
|
46
|
+
end
|
47
47
|
|
48
|
+
def send_message(msg)
|
49
|
+
logger.info "send message #{msg.class::COMMAND}"
|
50
|
+
send_data(msg.to_pkt)
|
48
51
|
end
|
49
52
|
|
50
53
|
private
|
@@ -56,9 +59,8 @@ module Bitcoin
|
|
56
59
|
# start handshake
|
57
60
|
def begin_handshake
|
58
61
|
logger.info "begin handshake with #{remote_node}"
|
59
|
-
ver = Bitcoin::Message::Version.new(remote_addr: remote_node, start_height:
|
60
|
-
|
61
|
-
send_data(ver.to_pkt)
|
62
|
+
ver = Bitcoin::Message::Version.new(remote_addr: remote_node, start_height: 0) # TODO use start_height in wallet
|
63
|
+
send_message(ver)
|
62
64
|
end
|
63
65
|
|
64
66
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
COIN = 100_000_000
|
4
|
+
MAX_MONEY = 21_000_000 * COIN
|
5
|
+
|
6
|
+
# The maximum allowed size for a serialized block, in bytes (only for buffer size limits)
|
7
|
+
MAX_BLOCK_SERIALIZED_SIZE = 4_000_000
|
8
|
+
# The maximum allowed weight for a block, see BIP 141 (network rule)
|
9
|
+
MAX_BLOCK_WEIGHT = 4_000_000
|
10
|
+
# The maximum allowed number of signature check operations in a block (network rule)
|
11
|
+
MAX_BLOCK_SIGOPS_COST = 80_000
|
12
|
+
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
|
13
|
+
COINBASE_MATURITY = 100
|
14
|
+
WITNESS_SCALE_FACTOR = 4
|
15
|
+
|
16
|
+
# 60 is the lower bound for the size of a valid serialized Tx
|
17
|
+
MIN_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 60
|
18
|
+
# 10 is the lower bound for the size of a serialized Tx
|
19
|
+
MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 10
|
20
|
+
|
21
|
+
# Flags for nSequence and nLockTime locks
|
22
|
+
LOCKTIME_VERIFY_SEQUENCE = (1 << 0)
|
23
|
+
LOCKTIME_MEDIAN_TIME_PAST = (1 << 1)
|
24
|
+
|
25
|
+
# script verify flags
|
26
|
+
SCRIPT_VERIFY_NONE = 0
|
27
|
+
SCRIPT_VERIFY_P2SH = (1 << 0)
|
28
|
+
SCRIPT_VERIFY_STRICTENC = (1 << 1)
|
29
|
+
SCRIPT_VERIFY_DERSIG = (1 << 2)
|
30
|
+
SCRIPT_VERIFY_LOW_S = (1 << 3)
|
31
|
+
SCRIPT_VERIFY_NULLDUMMY = (1 << 4)
|
32
|
+
SCRIPT_VERIFY_SIGPUSHONLY = (1 << 5)
|
33
|
+
SCRIPT_VERIFY_MINIMALDATA = (1 << 6)
|
34
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7)
|
35
|
+
SCRIPT_VERIFY_CLEANSTACK = (1 << 8)
|
36
|
+
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1 << 9) # Verify CHECKLOCKTIMEVERIFY (BIP-65)
|
37
|
+
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1 << 10) # support CHECKSEQUENCEVERIFY opcode (BIP-112)
|
38
|
+
SCRIPT_VERIFY_WITNESS = (1 << 11) # Support segregated witness
|
39
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1 << 12) # Making v1-v16 witness program non-standard
|
40
|
+
SCRIPT_VERIFY_MINIMALIF = (1 << 13) # Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
|
41
|
+
SCRIPT_VERIFY_NULLFAIL = (1 << 14) # Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
|
42
|
+
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1 << 15) # Public keys in segregated witness scripts must be compressed
|
43
|
+
|
44
|
+
MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH
|
45
|
+
|
46
|
+
# Standard script verification flags that standard transactions will comply with.
|
47
|
+
STANDARD_SCRIPT_VERIFY_FLAGS = [MANDATORY_SCRIPT_VERIFY_FLAGS,
|
48
|
+
SCRIPT_VERIFY_DERSIG,
|
49
|
+
SCRIPT_VERIFY_STRICTENC,
|
50
|
+
SCRIPT_VERIFY_MINIMALDATA,
|
51
|
+
SCRIPT_VERIFY_NULLDUMMY,
|
52
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
|
53
|
+
SCRIPT_VERIFY_CLEANSTACK,
|
54
|
+
SCRIPT_VERIFY_MINIMALIF,
|
55
|
+
SCRIPT_VERIFY_NULLFAIL,
|
56
|
+
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
|
57
|
+
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
|
58
|
+
SCRIPT_VERIFY_LOW_S,
|
59
|
+
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM]
|
60
|
+
|
61
|
+
# for script
|
62
|
+
|
63
|
+
# witness version
|
64
|
+
WITNESS_VERSION = 0x00
|
65
|
+
|
66
|
+
# Maximum script length in bytes
|
67
|
+
MAX_SCRIPT_SIZE = 10000
|
68
|
+
|
69
|
+
# Maximum number of public keys per multisig
|
70
|
+
MAX_PUBKEYS_PER_MULTISIG = 20
|
71
|
+
|
72
|
+
# Maximum number of non-push operations per script
|
73
|
+
MAX_OPS_PER_SCRIPT = 201
|
74
|
+
|
75
|
+
# Maximum number of bytes pushable to the stack
|
76
|
+
MAX_SCRIPT_ELEMENT_SIZE = 520
|
77
|
+
|
78
|
+
# Maximum number of size in the stack
|
79
|
+
MAX_STACK_SIZE = 1000
|
80
|
+
|
81
|
+
# Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
|
82
|
+
LOCKTIME_THRESHOLD = 500000000
|
83
|
+
|
84
|
+
# Signature hash types/flags
|
85
|
+
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
86
|
+
|
87
|
+
# Maximum number length in bytes
|
88
|
+
DEFAULT_MAX_NUM_SIZE = 4
|
89
|
+
|
90
|
+
# 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
|
91
|
+
MAX_OP_RETURN_RELAY = 83
|
92
|
+
|
93
|
+
SIG_VERSION = [:base, :witness_v0]
|
94
|
+
|
95
|
+
# for script error
|
96
|
+
SCRIPT_ERR_OK = 0
|
97
|
+
SCRIPT_ERR_UNKNOWN_ERROR = 1
|
98
|
+
SCRIPT_ERR_EVAL_FALSE = 2
|
99
|
+
SCRIPT_ERR_OP_RETURN = 3
|
100
|
+
|
101
|
+
# Max sizes
|
102
|
+
SCRIPT_ERR_SCRIPT_SIZE = 10
|
103
|
+
SCRIPT_ERR_PUSH_SIZE = 11
|
104
|
+
SCRIPT_ERR_OP_COUNT = 12
|
105
|
+
SCRIPT_ERR_STACK_SIZE = 13
|
106
|
+
SCRIPT_ERR_SIG_COUNT = 14
|
107
|
+
SCRIPT_ERR_PUBKEY_COUNT = 15
|
108
|
+
|
109
|
+
# Failed verify operations
|
110
|
+
SCRIPT_ERR_VERIFY = 20
|
111
|
+
SCRIPT_ERR_EQUALVERIFY = 21
|
112
|
+
SCRIPT_ERR_CHECKMULTISIGVERIFY = 22
|
113
|
+
SCRIPT_ERR_CHECKSIGVERIFY = 23
|
114
|
+
SCRIPT_ERR_NUMEQUALVERIFY = 24
|
115
|
+
|
116
|
+
# Logical/Format/Canonical errors
|
117
|
+
SCRIPT_ERR_BAD_OPCODE = 30
|
118
|
+
SCRIPT_ERR_DISABLED_OPCODE = 31
|
119
|
+
SCRIPT_ERR_INVALID_STACK_OPERATION = 32
|
120
|
+
SCRIPT_ERR_INVALID_ALTSTACK_OPERATION = 33
|
121
|
+
SCRIPT_ERR_UNBALANCED_CONDITIONAL = 34
|
122
|
+
|
123
|
+
# CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
|
124
|
+
SCRIPT_ERR_NEGATIVE_LOCKTIME = 40
|
125
|
+
SCRIPT_ERR_UNSATISFIED_LOCKTIME = 41
|
126
|
+
|
127
|
+
# Malleability
|
128
|
+
SCRIPT_ERR_SIG_HASHTYPE = 50
|
129
|
+
SCRIPT_ERR_SIG_DER = 51
|
130
|
+
SCRIPT_ERR_MINIMALDATA = 52
|
131
|
+
SCRIPT_ERR_SIG_PUSHONLY = 53
|
132
|
+
SCRIPT_ERR_SIG_HIGH_S = 54
|
133
|
+
SCRIPT_ERR_SIG_NULLDUMMY = 55
|
134
|
+
SCRIPT_ERR_PUBKEYTYPE = 56
|
135
|
+
SCRIPT_ERR_CLEANSTACK = 56
|
136
|
+
SCRIPT_ERR_MINIMALIF = 57
|
137
|
+
SCRIPT_ERR_SIG_NULLFAIL = 58
|
138
|
+
|
139
|
+
# softfork safeness
|
140
|
+
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
|
141
|
+
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = 61
|
142
|
+
|
143
|
+
# segregated witness
|
144
|
+
SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH = 70
|
145
|
+
SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY = 71
|
146
|
+
SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH = 72
|
147
|
+
SCRIPT_ERR_WITNESS_MALLEATED = 73
|
148
|
+
SCRIPT_ERR_WITNESS_MALLEATED_P2SH = 74
|
149
|
+
SCRIPT_ERR_WITNESS_UNEXPECTED = 75
|
150
|
+
SCRIPT_ERR_WITNESS_PUBKEYTYPE = 76
|
151
|
+
|
152
|
+
SCRIPT_ERR_ERROR_COUNT = 80
|
153
|
+
|
154
|
+
ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
|
155
|
+
NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
|
156
|
+
|
157
|
+
end
|
data/lib/bitcoin/key.rb
CHANGED
@@ -6,9 +6,10 @@ module Bitcoin
|
|
6
6
|
attr_accessor :priv_key
|
7
7
|
attr_accessor :pubkey
|
8
8
|
attr_accessor :compressed
|
9
|
+
attr_reader :secp256k1_module
|
9
10
|
|
10
11
|
def initialize(priv_key: nil, pubkey: nil, compressed: true)
|
11
|
-
|
12
|
+
@secp256k1_module = Bitcoin.secp_impl
|
12
13
|
@priv_key = priv_key
|
13
14
|
if pubkey
|
14
15
|
@pubkey = pubkey
|
@@ -40,7 +41,7 @@ module Bitcoin
|
|
40
41
|
|
41
42
|
# sign +data+ with private key
|
42
43
|
def sign(data)
|
43
|
-
sign_data(data, priv_key)
|
44
|
+
secp256k1_module.sign_data(data, priv_key)
|
44
45
|
end
|
45
46
|
|
46
47
|
# verify signature using public key
|
@@ -48,7 +49,7 @@ module Bitcoin
|
|
48
49
|
# @param [String] origin original message
|
49
50
|
# @return [Boolean] verify result
|
50
51
|
def verify(sig, origin)
|
51
|
-
verify_sig(origin, sig, pubkey)
|
52
|
+
secp256k1_module.verify_sig(origin, sig, pubkey)
|
52
53
|
end
|
53
54
|
|
54
55
|
# get pay to pubkey hash address
|
@@ -126,6 +127,17 @@ module Bitcoin
|
|
126
127
|
0
|
127
128
|
end
|
128
129
|
|
130
|
+
# generate publick key from private key
|
131
|
+
# @param [String] privkey a private key with string format
|
132
|
+
# @param [Boolean] compressed pubkey compressed?
|
133
|
+
# @return [String] a pubkey which generate from privkey
|
134
|
+
def generate_pubkey(privkey, compressed: true)
|
135
|
+
private_key = ECDSA::Format::IntegerOctetString.decode(privkey.htb)
|
136
|
+
public_key = ECDSA::Group::Secp256k1.generator.multiply_by_scalar(private_key)
|
137
|
+
pubkey = ECDSA::Format::PointOctetString.encode(public_key, compression: compressed)
|
138
|
+
pubkey.bth
|
139
|
+
end
|
140
|
+
|
129
141
|
end
|
130
142
|
|
131
143
|
end
|
data/lib/bitcoin/message/base.rb
CHANGED
@@ -22,18 +22,6 @@ module Bitcoin
|
|
22
22
|
raise 'to_payload must be implemented in a child class.'
|
23
23
|
end
|
24
24
|
|
25
|
-
def to_json
|
26
|
-
to_h.to_json
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_h
|
30
|
-
instance_variables.inject({}) do |result, var|
|
31
|
-
key = var.to_s
|
32
|
-
key.slice!(0) if key.start_with?('@')
|
33
|
-
result.update(key => instance_variable_get(var))
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
25
|
end
|
38
26
|
|
39
27
|
end
|
@@ -58,6 +58,8 @@ module Bitcoin
|
|
58
58
|
on_version(Version.parse_from_payload(payload))
|
59
59
|
when VerAck::COMMAND
|
60
60
|
on_ver_ack
|
61
|
+
when GetAddr::COMMAND
|
62
|
+
on_get_addr
|
61
63
|
when Addr::COMMAND
|
62
64
|
on_addr(Addr.parse_from_payload(payload))
|
63
65
|
when SendHeaders::COMMAND
|
@@ -84,6 +86,8 @@ module Bitcoin
|
|
84
86
|
on_reject(Reject.parse_from_payload(payload))
|
85
87
|
when SendCmpct::COMMAND
|
86
88
|
on_send_cmpct(SendCmpct.parse_from_payload(payload))
|
89
|
+
when Inv::COMMAND
|
90
|
+
on_inv(Inv.parse_from_payload(payload))
|
87
91
|
else
|
88
92
|
logger.warn("unsupported command received. #{command}")
|
89
93
|
conn.close("with command #{command}")
|
@@ -91,8 +95,8 @@ module Bitcoin
|
|
91
95
|
end
|
92
96
|
|
93
97
|
def on_version(version)
|
94
|
-
logger.info("receive version message. #{version.
|
95
|
-
conn.
|
98
|
+
logger.info("receive version message. #{version.build_json}")
|
99
|
+
conn.send_message(VerAck.new)
|
96
100
|
end
|
97
101
|
|
98
102
|
def on_ver_ack
|
@@ -100,8 +104,12 @@ module Bitcoin
|
|
100
104
|
conn.handshake_done
|
101
105
|
end
|
102
106
|
|
107
|
+
def on_get_addr
|
108
|
+
logger.info('receive getaddr message.')
|
109
|
+
end
|
110
|
+
|
103
111
|
def on_addr(addr)
|
104
|
-
logger.info("receive addr message. #{addr.
|
112
|
+
logger.info("receive addr message. #{addr.build_json}")
|
105
113
|
# TODO
|
106
114
|
end
|
107
115
|
|
@@ -111,42 +119,42 @@ module Bitcoin
|
|
111
119
|
end
|
112
120
|
|
113
121
|
def on_fee_filter(fee_filter)
|
114
|
-
logger.info("receive feefilter message. #{fee_filter.
|
122
|
+
logger.info("receive feefilter message. #{fee_filter.build_json}")
|
115
123
|
conn.fee_rate = fee_filter.fee_rate
|
116
124
|
end
|
117
125
|
|
118
126
|
def on_ping(ping)
|
119
|
-
logger.info("receive ping message. #{ping.
|
120
|
-
conn.
|
127
|
+
logger.info("receive ping message. #{ping.build_json}")
|
128
|
+
conn.send_message(ping.to_response)
|
121
129
|
end
|
122
130
|
|
123
131
|
def on_pong(pong)
|
124
|
-
logger.info("receive pong message. #{pong.
|
132
|
+
logger.info("receive pong message. #{pong.build_json}")
|
125
133
|
# TODO calculate response
|
126
134
|
end
|
127
135
|
|
128
136
|
def on_get_headers(headers)
|
129
|
-
logger.info("receive getheaders message. #{headers.
|
137
|
+
logger.info("receive getheaders message. #{headers.build_json}")
|
130
138
|
# TODO
|
131
139
|
end
|
132
140
|
|
133
141
|
def on_headers(headers)
|
134
|
-
logger.info("receive headers message. #{headers.
|
142
|
+
logger.info("receive headers message. #{headers.build_json}")
|
135
143
|
# TODO
|
136
144
|
end
|
137
145
|
|
138
146
|
def on_block(block)
|
139
|
-
logger.info("receive block message. #{block.
|
147
|
+
logger.info("receive block message. #{block.build_json}")
|
140
148
|
# TODO
|
141
149
|
end
|
142
150
|
|
143
151
|
def on_tx(tx)
|
144
|
-
logger.info("receive tx message. #{tx.
|
152
|
+
logger.info("receive tx message. #{tx.build_json}")
|
145
153
|
# TODO
|
146
154
|
end
|
147
155
|
|
148
156
|
def on_not_found(not_found)
|
149
|
-
logger.info("receive notfound message. #{not_found.
|
157
|
+
logger.info("receive notfound message. #{not_found.build_json}")
|
150
158
|
# TODO
|
151
159
|
end
|
152
160
|
|
@@ -156,15 +164,20 @@ module Bitcoin
|
|
156
164
|
end
|
157
165
|
|
158
166
|
def on_reject(reject)
|
159
|
-
logger.warn("receive reject message. #{reject.
|
167
|
+
logger.warn("receive reject message. #{reject.build_json}")
|
160
168
|
# TODO
|
161
169
|
end
|
162
170
|
|
163
171
|
def on_send_cmpct(cmpct)
|
164
|
-
logger.info("receive sendcmpct message. #{cmpct.
|
172
|
+
logger.info("receive sendcmpct message. #{cmpct.build_json}")
|
165
173
|
# TODO if mode is high and version is 1, relay block with cmpctblock message
|
166
174
|
end
|
167
175
|
|
176
|
+
def on_inv(inv)
|
177
|
+
logger.info("receive inv message. #{inv.build_json}")
|
178
|
+
# TODO
|
179
|
+
end
|
180
|
+
|
168
181
|
end
|
169
182
|
end
|
170
183
|
end
|
@@ -19,7 +19,7 @@ module Bitcoin
|
|
19
19
|
|
20
20
|
def initialize(opts = {})
|
21
21
|
@version = Bitcoin.chain_params.protocol_version
|
22
|
-
@services =
|
22
|
+
@services = DEFAULT_SERVICE_FLAGS
|
23
23
|
@timestamp = Time.now.to_i
|
24
24
|
@local_addr = "127.0.0.1:#{Bitcoin.chain_params.default_port}"
|
25
25
|
@remote_addr = "127.0.0.1:#{Bitcoin.chain_params.default_port}"
|
data/lib/bitcoin/message.rb
CHANGED
@@ -34,7 +34,16 @@ module Bitcoin
|
|
34
34
|
HEADER_SIZE = 24
|
35
35
|
USER_AGENT = "/bitcoinrb:#{Bitcoin::VERSION}/"
|
36
36
|
|
37
|
-
SERVICE_FLAGS = {
|
37
|
+
SERVICE_FLAGS = {
|
38
|
+
none: 0,
|
39
|
+
network: 1 << 0, # the node is capable of serving the block chain. It is currently set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want network services but don't provide them.
|
40
|
+
# getutxo: 1 << 1, # BIP-64. not implemented in Bitcoin Core.
|
41
|
+
bloom: 1 << 2, # the node is capable and willing to handle bloom-filtered connections. Bitcoin Core nodes used to support this by default, without advertising this bit, but no longer do as of protocol version 70011 (= NO_BLOOM_VERSION)
|
42
|
+
witness: 1 << 3, # the node can be asked for blocks and transactions including witness data.
|
43
|
+
# xthin: 1 << 4 # support Xtreme Thinblocks. not implemented in Bitcoin Core
|
44
|
+
}
|
45
|
+
|
46
|
+
DEFAULT_SERVICE_FLAGS = SERVICE_FLAGS[:network] | SERVICE_FLAGS[:bloom] | SERVICE_FLAGS[:witness]
|
38
47
|
|
39
48
|
DEFAULT_STOP_HASH = "00"*32
|
40
49
|
|
data/lib/bitcoin/opcodes.rb
CHANGED
@@ -116,8 +116,8 @@ module Bitcoin
|
|
116
116
|
OP_CHECKMULTISIGVERIFY = 0xaf
|
117
117
|
|
118
118
|
# https://en.bitcoin.it/wiki/Script#Locktime
|
119
|
-
|
120
|
-
|
119
|
+
OP_NOP2 = OP_CHECKLOCKTIMEVERIFY = 0xb1
|
120
|
+
OP_NOP3 = OP_CHECKSEQUENCEVERIFY = 0xb2
|
121
121
|
|
122
122
|
# https://en.bitcoin.it/wiki/Script#Reserved_words
|
123
123
|
OP_RESERVED = 0x50
|
data/lib/bitcoin/out_point.rb
CHANGED
@@ -9,7 +9,7 @@ module Bitcoin
|
|
9
9
|
attr_reader :hash
|
10
10
|
attr_reader :index
|
11
11
|
|
12
|
-
def initialize(hash, index)
|
12
|
+
def initialize(hash, index = -1)
|
13
13
|
@hash = hash
|
14
14
|
@index = index
|
15
15
|
end
|
@@ -26,6 +26,10 @@ module Bitcoin
|
|
26
26
|
new(COINBASE_HASH, COINBASE_INDEX)
|
27
27
|
end
|
28
28
|
|
29
|
+
def valid?
|
30
|
+
index >= 0 && (!coinbase? && hash != COINBASE_HASH)
|
31
|
+
end
|
32
|
+
|
29
33
|
end
|
30
34
|
|
31
35
|
end
|
@@ -4,33 +4,6 @@ module Bitcoin
|
|
4
4
|
class Script
|
5
5
|
include Bitcoin::Opcodes
|
6
6
|
|
7
|
-
# witness version
|
8
|
-
WITNESS_VERSION = 0x00
|
9
|
-
|
10
|
-
# Maximum script length in bytes
|
11
|
-
MAX_SCRIPT_SIZE = 10000
|
12
|
-
|
13
|
-
# Maximum number of public keys per multisig
|
14
|
-
MAX_PUBKEYS_PER_MULTISIG = 20
|
15
|
-
|
16
|
-
# Maximum number of non-push operations per script
|
17
|
-
MAX_OPS_PER_SCRIPT = 201
|
18
|
-
|
19
|
-
# Maximum number of bytes pushable to the stack
|
20
|
-
MAX_SCRIPT_ELEMENT_SIZE = 520
|
21
|
-
|
22
|
-
# Maximum number of size in the stack
|
23
|
-
MAX_STACK_SIZE = 1000
|
24
|
-
|
25
|
-
# Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
|
26
|
-
LOCKTIME_THRESHOLD = 500000000
|
27
|
-
|
28
|
-
# Signature hash types/flags
|
29
|
-
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
30
|
-
|
31
|
-
# Maximum number length in bytes
|
32
|
-
DEFAULT_MAX_NUM_SIZE = 4
|
33
|
-
|
34
7
|
attr_accessor :chunks
|
35
8
|
|
36
9
|
def initialize
|
@@ -53,8 +26,13 @@ module Bitcoin
|
|
53
26
|
# @return [Script, Script] first element is p2sh script, second one is redeem script.
|
54
27
|
def self.to_p2sh_multisig_script(m, pubkeys)
|
55
28
|
redeem_script = to_multisig_script(m, pubkeys)
|
56
|
-
|
57
|
-
|
29
|
+
[redeem_script.to_p2sh, redeem_script]
|
30
|
+
end
|
31
|
+
|
32
|
+
# generate p2sh script with this as a redeem script
|
33
|
+
# @return [Script] P2SH script
|
34
|
+
def to_p2sh
|
35
|
+
Script.new << OP_HASH160 << to_hash160 << OP_EQUAL
|
58
36
|
end
|
59
37
|
|
60
38
|
# generate m of n multisig script
|
@@ -93,17 +71,27 @@ module Bitcoin
|
|
93
71
|
opcode = buf.read(1)
|
94
72
|
if opcode.pushdata?
|
95
73
|
pushcode = opcode.ord
|
74
|
+
packed_size = nil
|
96
75
|
len = case pushcode
|
97
76
|
when OP_PUSHDATA1
|
98
|
-
buf.read(1)
|
77
|
+
packed_size = buf.read(1)
|
78
|
+
packed_size.unpack('C').first
|
99
79
|
when OP_PUSHDATA2
|
100
|
-
buf.read(2)
|
80
|
+
packed_size = buf.read(2)
|
81
|
+
packed_size.unpack('v').first
|
101
82
|
when OP_PUSHDATA4
|
102
|
-
buf.read(4)
|
83
|
+
packed_size = buf.read(4)
|
84
|
+
packed_size.unpack('V').first
|
103
85
|
else
|
104
86
|
pushcode if pushcode < OP_PUSHDATA1
|
105
87
|
end
|
106
|
-
|
88
|
+
if len
|
89
|
+
s.chunks << [len].pack('C') if buf.eof?
|
90
|
+
unless buf.eof?
|
91
|
+
chunk = (packed_size ? (opcode + packed_size) : (opcode)) + buf.read(len)
|
92
|
+
s.chunks << chunk
|
93
|
+
end
|
94
|
+
end
|
107
95
|
else
|
108
96
|
s << opcode.ord
|
109
97
|
end
|
@@ -115,6 +103,10 @@ module Bitcoin
|
|
115
103
|
chunks.join
|
116
104
|
end
|
117
105
|
|
106
|
+
def to_hex
|
107
|
+
to_payload.bth
|
108
|
+
end
|
109
|
+
|
118
110
|
def to_addr
|
119
111
|
return p2pkh_addr if p2pkh?
|
120
112
|
return p2wpkh_addr if p2wpkh?
|
@@ -122,6 +114,11 @@ module Bitcoin
|
|
122
114
|
return p2sh_addr if p2sh?
|
123
115
|
end
|
124
116
|
|
117
|
+
# check whether standard script.
|
118
|
+
def standard?
|
119
|
+
p2pkh? | p2sh? | p2wpkh? | p2wsh? | multisig? | standard_op_return?
|
120
|
+
end
|
121
|
+
|
125
122
|
# whether this script is a P2PKH format script.
|
126
123
|
def p2pkh?
|
127
124
|
return false unless chunks.size == 5
|
@@ -145,8 +142,25 @@ module Bitcoin
|
|
145
142
|
OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
|
146
143
|
end
|
147
144
|
|
145
|
+
def multisig?
|
146
|
+
return false if chunks.size < 4 || chunks.last.ord != OP_CHECKMULTISIG
|
147
|
+
pubkey_count = Opcodes.opcode_to_small_int(chunks[-2].opcode)
|
148
|
+
sig_count = Opcodes.opcode_to_small_int(chunks[0].opcode)
|
149
|
+
return false unless pubkey_count || sig_count
|
150
|
+
sig_count < pubkey_count
|
151
|
+
end
|
152
|
+
|
153
|
+
def op_return?
|
154
|
+
chunks.size >= 1 && chunks[0].ord == OP_RETURN
|
155
|
+
end
|
156
|
+
|
157
|
+
def standard_op_return?
|
158
|
+
op_return? && size <= MAX_OP_RETURN_RELAY &&
|
159
|
+
(chunks.size == 1 || chunks[1].opcode <= OP_16)
|
160
|
+
end
|
161
|
+
|
148
162
|
# whether data push only script which dose not include other opcode
|
149
|
-
def
|
163
|
+
def push_only?
|
150
164
|
chunks.each do |c|
|
151
165
|
return false if !c.opcode.nil? && c.opcode > OP_16
|
152
166
|
end
|
@@ -156,11 +170,30 @@ module Bitcoin
|
|
156
170
|
# A witness program is any valid Script that consists of a 1-byte push opcode followed by a data push between 2 and 40 bytes.
|
157
171
|
def witness_program?
|
158
172
|
return false if size < 4 || size > 42 || chunks.size < 2
|
173
|
+
|
159
174
|
opcode = chunks[0].opcode
|
175
|
+
|
160
176
|
return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
|
161
177
|
return false unless chunks[1].pushdata?
|
162
|
-
|
163
|
-
|
178
|
+
|
179
|
+
if size == (chunks[1][0].unpack('C').first + 2)
|
180
|
+
program_size = chunks[1].pushed_data.bytesize
|
181
|
+
return program_size >= 2 && program_size <= 40
|
182
|
+
end
|
183
|
+
|
184
|
+
false
|
185
|
+
end
|
186
|
+
|
187
|
+
# If this script is witness program, return its script code,
|
188
|
+
# otherwise returns the self payload. ScriptInterpreter does not use this.
|
189
|
+
def to_script_code(skip_separator_index = 0)
|
190
|
+
payload = to_payload
|
191
|
+
if p2wpkh?
|
192
|
+
payload = Script.to_p2pkh(chunks[1].pushed_data.bth).to_payload
|
193
|
+
elsif skip_separator_index > 0
|
194
|
+
payload = subscript_codeseparator(skip_separator_index)
|
195
|
+
end
|
196
|
+
Bitcoin.pack_var_string(payload)
|
164
197
|
end
|
165
198
|
|
166
199
|
# get witness version and witness program
|
@@ -295,8 +328,54 @@ module Bitcoin
|
|
295
328
|
# removes chunks matching subscript byte-for-byte and returns as a new object.
|
296
329
|
def find_and_delete(subscript)
|
297
330
|
raise ArgumentError, 'subscript must be Bitcoin::Script' unless subscript.is_a?(Script)
|
298
|
-
|
299
|
-
|
331
|
+
return self if subscript.chunks.empty?
|
332
|
+
buf = []
|
333
|
+
i = 0
|
334
|
+
result = Script.new
|
335
|
+
chunks.each do |chunk|
|
336
|
+
sub_chunk = subscript.chunks[i]
|
337
|
+
if chunk.start_with?(sub_chunk)
|
338
|
+
if chunk == sub_chunk
|
339
|
+
buf << chunk
|
340
|
+
i += 1
|
341
|
+
(i = 0; buf.clear) if i == subscript.chunks.size # matched the whole subscript
|
342
|
+
else # matched the part of head
|
343
|
+
i = 0
|
344
|
+
tmp = chunk.dup
|
345
|
+
tmp.slice!(sub_chunk)
|
346
|
+
result.chunks << tmp
|
347
|
+
end
|
348
|
+
else
|
349
|
+
result.chunks << buf.join unless buf.empty?
|
350
|
+
if buf.first == chunk
|
351
|
+
i = 1
|
352
|
+
buf = [chunk]
|
353
|
+
else
|
354
|
+
i = 0
|
355
|
+
result.chunks << chunk
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
result
|
360
|
+
end
|
361
|
+
|
362
|
+
# remove all occurences of opcode. Typically it's OP_CODESEPARATOR.
|
363
|
+
def delete_opcode(opcode)
|
364
|
+
@chunks = chunks.select{|chunk| chunk.ord != opcode}
|
365
|
+
self
|
366
|
+
end
|
367
|
+
|
368
|
+
# Returns a script that deleted the script before the index specified by separator_index.
|
369
|
+
def subscript_codeseparator(separator_index)
|
370
|
+
buf = []
|
371
|
+
process_separator_index = 0
|
372
|
+
chunks.each{|chunk|
|
373
|
+
buf << chunk if process_separator_index == separator_index
|
374
|
+
if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
|
375
|
+
process_separator_index += 1
|
376
|
+
end
|
377
|
+
}
|
378
|
+
buf.join
|
300
379
|
end
|
301
380
|
|
302
381
|
def ==(other)
|