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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 96f0a34fb5dcdaef395f91d69a4d9b23b0ef0e9a
4
- data.tar.gz: 54cb847c45147461350da5bf2f6f7aa88e129858
3
+ metadata.gz: a527d6ef43eda02e2ef56d64edb5da7dfa04a956
4
+ data.tar.gz: b1a7902b2be175f6f771ca3a418cbf869ab9dc7e
5
5
  SHA512:
6
- metadata.gz: a151846d7ea230bd203dab08181dec34052cce1700c505a123b8220d80f1f7ff7fa296b90bbd5c639a725bb9e756661a0c77449b1731843e60526c793b8436c0
7
- data.tar.gz: 87dbd90b24338fb0088f92e5bc1083e2749cdffa5729395433371808e20e74126ed395e01f3eaa5b73e3a35d7211ddd1137c3e7ca77a35c951531f809f32c3f3
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"
@@ -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}, data : #{data}"
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
- # send_data(Message::SendCmpct.new(0, 1))
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: 1150660)
60
- logger.info "send version message. #{ver.to_json}"
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
- extend Bitcoin.secp_impl
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
@@ -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.to_json}")
95
- conn.send_data(VerAck.new.to_pkt)
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.to_json}")
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.to_json}")
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.to_json}")
120
- conn.send_data(ping.to_response)
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.to_json}")
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.to_json}")
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.to_json}")
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.to_json}")
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.to_json}")
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.to_json}")
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.to_json}")
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.to_json}")
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 = Bitcoin::Message::SERVICE_FLAGS[:network]
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}"
@@ -34,7 +34,16 @@ module Bitcoin
34
34
  HEADER_SIZE = 24
35
35
  USER_AGENT = "/bitcoinrb:#{Bitcoin::VERSION}/"
36
36
 
37
- SERVICE_FLAGS = {none: 0, network: 1 << 0, getutxo: 1 << 1, bloom: 1 << 2, witness: 1 << 3, xthin: 1 << 4}
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
 
@@ -116,8 +116,8 @@ module Bitcoin
116
116
  OP_CHECKMULTISIGVERIFY = 0xaf
117
117
 
118
118
  # https://en.bitcoin.it/wiki/Script#Locktime
119
- OP_CHECKLOCKTIMEVERIFY = OP_NOP2 = 0xb1
120
- OP_CHECKSEQUENCEVERIFY = OP_NOP3 = 0xb2
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
@@ -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
- p2sh_script = new << OP_HASH160 << redeem_script.to_hash160 << OP_EQUAL
57
- [p2sh_script, redeem_script]
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).unpack('C').first
77
+ packed_size = buf.read(1)
78
+ packed_size.unpack('C').first
99
79
  when OP_PUSHDATA2
100
- buf.read(2).unpack('v').first
80
+ packed_size = buf.read(2)
81
+ packed_size.unpack('v').first
101
82
  when OP_PUSHDATA4
102
- buf.read(4).unpack('V').first
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
- s << buf.read(len).bth if len
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 data_only?
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
- program_size = chunks[1].pushed_data.bytesize
163
- program_size >= 2 && program_size <= 40
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
- diff = to_payload.bth.gsub(subscript.to_payload.bth, '')
299
- Script.parse_from_payload(diff.htb)
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)