bitcoinrb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bitcoinrb.gemspec +32 -0
- data/exe/bitcoinrb-cli +5 -0
- data/exe/bitcoinrbd +49 -0
- data/lib/bitcoin.rb +121 -0
- data/lib/bitcoin/base58.rb +40 -0
- data/lib/bitcoin/block_header.rb +41 -0
- data/lib/bitcoin/chain_params.rb +57 -0
- data/lib/bitcoin/chainparams/mainnet.yml +25 -0
- data/lib/bitcoin/chainparams/regtest.yml +20 -0
- data/lib/bitcoin/chainparams/testnet.yml +24 -0
- data/lib/bitcoin/connection.rb +66 -0
- data/lib/bitcoin/ext_key.rb +205 -0
- data/lib/bitcoin/key.rb +131 -0
- data/lib/bitcoin/logger.rb +18 -0
- data/lib/bitcoin/merkle_tree.rb +120 -0
- data/lib/bitcoin/message.rb +42 -0
- data/lib/bitcoin/message/addr.rb +74 -0
- data/lib/bitcoin/message/base.rb +40 -0
- data/lib/bitcoin/message/block.rb +41 -0
- data/lib/bitcoin/message/error.rb +10 -0
- data/lib/bitcoin/message/fee_filter.rb +27 -0
- data/lib/bitcoin/message/filter_add.rb +28 -0
- data/lib/bitcoin/message/filter_clear.rb +17 -0
- data/lib/bitcoin/message/filter_load.rb +43 -0
- data/lib/bitcoin/message/get_addr.rb +17 -0
- data/lib/bitcoin/message/get_blocks.rb +29 -0
- data/lib/bitcoin/message/get_data.rb +21 -0
- data/lib/bitcoin/message/get_headers.rb +28 -0
- data/lib/bitcoin/message/handler.rb +170 -0
- data/lib/bitcoin/message/headers.rb +34 -0
- data/lib/bitcoin/message/headers_parser.rb +24 -0
- data/lib/bitcoin/message/inv.rb +21 -0
- data/lib/bitcoin/message/inventories_parser.rb +23 -0
- data/lib/bitcoin/message/inventory.rb +47 -0
- data/lib/bitcoin/message/mem_pool.rb +17 -0
- data/lib/bitcoin/message/merkle_block.rb +42 -0
- data/lib/bitcoin/message/not_found.rb +29 -0
- data/lib/bitcoin/message/ping.rb +30 -0
- data/lib/bitcoin/message/pong.rb +26 -0
- data/lib/bitcoin/message/reject.rb +46 -0
- data/lib/bitcoin/message/send_cmpct.rb +43 -0
- data/lib/bitcoin/message/send_headers.rb +16 -0
- data/lib/bitcoin/message/tx.rb +30 -0
- data/lib/bitcoin/message/ver_ack.rb +17 -0
- data/lib/bitcoin/message/version.rb +79 -0
- data/lib/bitcoin/mnemonic.rb +76 -0
- data/lib/bitcoin/mnemonic/wordlist/chinese_simplified.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/chinese_traditional.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/english.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/french.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/italian.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/japanese.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/spanish.txt +2048 -0
- data/lib/bitcoin/nodes.rb +5 -0
- data/lib/bitcoin/nodes/spv.rb +13 -0
- data/lib/bitcoin/nodes/spv/cli.rb +12 -0
- data/lib/bitcoin/nodes/spv/daemon.rb +21 -0
- data/lib/bitcoin/opcodes.rb +172 -0
- data/lib/bitcoin/out_point.rb +31 -0
- data/lib/bitcoin/script/script.rb +347 -0
- data/lib/bitcoin/script/script_error.rb +168 -0
- data/lib/bitcoin/script/script_interpreter.rb +694 -0
- data/lib/bitcoin/script/tx_checker.rb +44 -0
- data/lib/bitcoin/script_witness.rb +29 -0
- data/lib/bitcoin/secp256k1.rb +10 -0
- data/lib/bitcoin/secp256k1/native.rb +22 -0
- data/lib/bitcoin/secp256k1/ruby.rb +96 -0
- data/lib/bitcoin/tx.rb +191 -0
- data/lib/bitcoin/tx_in.rb +45 -0
- data/lib/bitcoin/tx_out.rb +32 -0
- data/lib/bitcoin/util.rb +105 -0
- data/lib/bitcoin/version.rb +3 -0
- metadata +256 -0
@@ -0,0 +1,172 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# https://bitcoin.org/en/developer-reference#opcodes
|
4
|
+
module Opcodes
|
5
|
+
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# https://en.bitcoin.it/wiki/Script#Constants
|
9
|
+
OP_0 = 0x00
|
10
|
+
OP_1 = 0x51
|
11
|
+
OP_2 = 0x52
|
12
|
+
OP_3 = 0x53
|
13
|
+
OP_4 = 0x54
|
14
|
+
OP_5 = 0x55
|
15
|
+
OP_6 = 0x56
|
16
|
+
OP_7 = 0x57
|
17
|
+
OP_8 = 0x58
|
18
|
+
OP_9 = 0x59
|
19
|
+
OP_10 = 0x5a
|
20
|
+
OP_11 = 0x5b
|
21
|
+
OP_12 = 0x5c
|
22
|
+
OP_13 = 0x5d
|
23
|
+
OP_14 = 0x5e
|
24
|
+
OP_15 = 0x5f
|
25
|
+
OP_16 = 0x60
|
26
|
+
|
27
|
+
OP_PUSHDATA1 = 0x4c
|
28
|
+
OP_PUSHDATA2 = 0x4d
|
29
|
+
OP_PUSHDATA4 = 0x4e
|
30
|
+
OP_1NEGATE = 0x4f
|
31
|
+
|
32
|
+
# https://en.bitcoin.it/wiki/Script#Flow_control
|
33
|
+
OP_NOP = 0x61
|
34
|
+
OP_IF = 0x63
|
35
|
+
OP_NOTIF = 0x64
|
36
|
+
OP_ELSE = 0x67
|
37
|
+
OP_ENDIF = 0x68
|
38
|
+
OP_VERIFY = 0x69
|
39
|
+
OP_RETURN = 0x6a
|
40
|
+
|
41
|
+
# https://en.bitcoin.it/wiki/Script#Stack
|
42
|
+
OP_TOALTSTACK = 0x6b
|
43
|
+
OP_FROMALTSTACK = 0x6c
|
44
|
+
OP_IFDUP = 0x73
|
45
|
+
OP_DEPTH = 0x74
|
46
|
+
OP_DROP = 0x75
|
47
|
+
OP_DUP = 0x76
|
48
|
+
OP_NIP = 0x77
|
49
|
+
OP_OVER = 0x78
|
50
|
+
OP_PICK = 0x79
|
51
|
+
OP_ROLL = 0x7a
|
52
|
+
OP_ROT = 0x7b
|
53
|
+
OP_SWAP = 0x7c
|
54
|
+
OP_TUCK = 0x7d
|
55
|
+
OP_2DROP = 0x6d
|
56
|
+
OP_2DUP = 0x6e
|
57
|
+
OP_3DUP = 0x6f
|
58
|
+
OP_2OVER = 0x70
|
59
|
+
OP_2ROT = 0x71
|
60
|
+
OP_2SWAP = 0x72
|
61
|
+
|
62
|
+
# https://en.bitcoin.it/wiki/Script#Splice
|
63
|
+
OP_CAT = 0x7e # disabled
|
64
|
+
OP_SUBSTR = 0x7f # disabled
|
65
|
+
OP_LEFT = 0x80 # disabled
|
66
|
+
OP_RIGHT = 0x81 # disabled
|
67
|
+
OP_SIZE = 0x82
|
68
|
+
|
69
|
+
# https://en.bitcoin.it/wiki/Script#Bitwise_logic
|
70
|
+
OP_INVERT = 0x83 # disabled
|
71
|
+
OP_AND = 0x84 # disabled
|
72
|
+
OP_OR = 0x85 # disabled
|
73
|
+
OP_XOR = 0x86 # disabled
|
74
|
+
OP_EQUAL = 0x87
|
75
|
+
OP_EQUALVERIFY = 0x88
|
76
|
+
|
77
|
+
# https://en.bitcoin.it/wiki/Script#Arithmetic
|
78
|
+
OP_1ADD = 0x8b
|
79
|
+
OP_1SUB = 0x8c
|
80
|
+
OP_2MUL = 0x8d # disabled
|
81
|
+
OP_2DIV = 0x8e # disabled
|
82
|
+
OP_NEGATE = 0x8f
|
83
|
+
OP_ABS = 0x90
|
84
|
+
OP_NOT = 0x91
|
85
|
+
OP_0NOTEQUAL = 0x92
|
86
|
+
OP_ADD = 0x93
|
87
|
+
OP_SUB = 0x94
|
88
|
+
OP_MUL = 0x95 # disabled
|
89
|
+
OP_DIV = 0x96 # disabled
|
90
|
+
OP_MOD = 0x97 # disabled
|
91
|
+
OP_LSHIFT = 0x98 # disabled
|
92
|
+
OP_RSHIFT = 0x99 # disabled
|
93
|
+
OP_BOOLAND = 0x9a
|
94
|
+
OP_BOOLOR = 0x9b
|
95
|
+
OP_NUMEQUAL = 0x9c
|
96
|
+
OP_NUMEQUALVERIFY = 0x9d
|
97
|
+
OP_NUMNOTEQUAL = 0x9e
|
98
|
+
OP_LESSTHAN = 0x9f
|
99
|
+
OP_GREATERTHAN = 0xa0
|
100
|
+
OP_LESSTHANOREQUAL = 0xa1
|
101
|
+
OP_GREATERTHANOREQUAL = 0xa2
|
102
|
+
OP_MIN = 0xa3
|
103
|
+
OP_MAX = 0xa4
|
104
|
+
OP_WITHIN = 0xa5
|
105
|
+
|
106
|
+
# https://en.bitcoin.it/wiki/Script#Crypto
|
107
|
+
OP_RIPEMD160 = 0xa6
|
108
|
+
OP_SHA1 = 0xa7
|
109
|
+
OP_SHA256 = 0xa8
|
110
|
+
OP_HASH160 = 0xa9
|
111
|
+
OP_HASH256 = 0xaa
|
112
|
+
OP_CODESEPARATOR = 0xab
|
113
|
+
OP_CHECKSIG = 0xac
|
114
|
+
OP_CHECKSIGVERIFY= 0xad
|
115
|
+
OP_CHECKMULTISIG = 0xae
|
116
|
+
OP_CHECKMULTISIGVERIFY = 0xaf
|
117
|
+
|
118
|
+
# https://en.bitcoin.it/wiki/Script#Locktime
|
119
|
+
OP_CHECKLOCKTIMEVERIFY = OP_NOP2 = 0xb1
|
120
|
+
OP_CHECKSEQUENCEVERIFY = OP_NOP3 = 0xb2
|
121
|
+
|
122
|
+
# https://en.bitcoin.it/wiki/Script#Reserved_words
|
123
|
+
OP_RESERVED = 0x50
|
124
|
+
OP_VER = 0x62
|
125
|
+
OP_VERIF = 0x65
|
126
|
+
OP_VERNOTIF = 0x66
|
127
|
+
OP_RESERVED1= 0x89
|
128
|
+
OP_RESERVED2 = 0x8a
|
129
|
+
|
130
|
+
OP_NOP1 = 0xb0
|
131
|
+
OP_NOP4 = 0xb3
|
132
|
+
OP_NOP5 = 0xb4
|
133
|
+
OP_NOP6 = 0xb5
|
134
|
+
OP_NOP7 = 0xb6
|
135
|
+
OP_NOP8 = 0xb7
|
136
|
+
OP_NOP9 = 0xb8
|
137
|
+
OP_NOP10 = 0xb9
|
138
|
+
|
139
|
+
OPCODES_MAP = Hash[*constants.grep(/^OP_/).map { |c| [const_get(c), c.to_s] }.flatten]
|
140
|
+
NAME_MAP = Hash[*constants.grep(/^OP_/).map { |c| [c.to_s, const_get(c)] }.flatten]
|
141
|
+
|
142
|
+
def opcode_to_name(opcode)
|
143
|
+
return OPCODES_MAP[opcode].delete('OP_') if opcode == OP_0 || (opcode <= OP_16 && opcode >= OP_1)
|
144
|
+
OPCODES_MAP[opcode]
|
145
|
+
end
|
146
|
+
|
147
|
+
def name_to_opcode(name)
|
148
|
+
return NAME_MAP['OP_' + name] if name =~ /^\d/ && name.to_i < 17 && name.to_i > -1
|
149
|
+
NAME_MAP[name]
|
150
|
+
end
|
151
|
+
|
152
|
+
# whether opcode is predefined opcode
|
153
|
+
def defined?(opcode)
|
154
|
+
!opcode_to_name(opcode).nil?
|
155
|
+
end
|
156
|
+
|
157
|
+
def small_int_to_opcode(int)
|
158
|
+
return OP_0 if int == 0
|
159
|
+
return OP_1NEGATE if int == -1
|
160
|
+
return OP_1 + (int - 1) if int >= 1 && int <= 16
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
|
164
|
+
def opcode_to_small_int(opcode)
|
165
|
+
return 0 if opcode == ''.b || opcode == OP_0
|
166
|
+
return -1 if opcode == OP_1NEGATE
|
167
|
+
return opcode - (OP_1 - 1) if opcode >= OP_1 && opcode <= OP_16
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# outpoint class
|
4
|
+
class OutPoint
|
5
|
+
|
6
|
+
COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
|
7
|
+
COINBASE_INDEX = 4294967295
|
8
|
+
|
9
|
+
attr_reader :hash
|
10
|
+
attr_reader :index
|
11
|
+
|
12
|
+
def initialize(hash, index)
|
13
|
+
@hash = hash
|
14
|
+
@index = index
|
15
|
+
end
|
16
|
+
|
17
|
+
def coinbase?
|
18
|
+
hash == COINBASE_HASH && index == COINBASE_INDEX
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_payload
|
22
|
+
[hash.htb.reverse, index].pack('a32V')
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.create_coinbase_outpoint
|
26
|
+
new(COINBASE_HASH, COINBASE_INDEX)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,347 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# bitcoin script
|
4
|
+
class Script
|
5
|
+
include Bitcoin::Opcodes
|
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
|
+
attr_accessor :chunks
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@chunks = []
|
38
|
+
end
|
39
|
+
|
40
|
+
# generate P2PKH script
|
41
|
+
def self.to_p2pkh(pubkey_hash)
|
42
|
+
new << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
|
43
|
+
end
|
44
|
+
|
45
|
+
# generate P2WPKH script
|
46
|
+
def self.to_p2wpkh(pubkey_hash)
|
47
|
+
new << WITNESS_VERSION << pubkey_hash
|
48
|
+
end
|
49
|
+
|
50
|
+
# generate m of n multisig p2sh script
|
51
|
+
# @param [String] m the number of signatures required for multisig
|
52
|
+
# @param [Array] pubkeys array of public keys that compose multisig
|
53
|
+
# @return [Script, Script] first element is p2sh script, second one is redeem script.
|
54
|
+
def self.to_p2sh_multisig_script(m, pubkeys)
|
55
|
+
redeem_script = to_multisig_script(m, pubkeys)
|
56
|
+
p2sh_script = new << OP_HASH160 << redeem_script.to_hash160 << OP_EQUAL
|
57
|
+
[p2sh_script, redeem_script]
|
58
|
+
end
|
59
|
+
|
60
|
+
# generate m of n multisig script
|
61
|
+
# @param [String] m the number of signatures required for multisig
|
62
|
+
# @param [Array] pubkeys array of public keys that compose multisig
|
63
|
+
# @return [Script] multisig script.
|
64
|
+
def self.to_multisig_script(m, pubkeys)
|
65
|
+
new << m << pubkeys << pubkeys.size << OP_CHECKMULTISIG
|
66
|
+
end
|
67
|
+
|
68
|
+
# generate p2wsh script for +redeem_script+
|
69
|
+
# @param [Script] redeem_script target redeem script
|
70
|
+
# @param [Script] p2wsh script
|
71
|
+
def self.to_p2wsh(redeem_script)
|
72
|
+
new << WITNESS_VERSION << redeem_script.to_sha256
|
73
|
+
end
|
74
|
+
|
75
|
+
# generate script from string.
|
76
|
+
def self.from_string(string)
|
77
|
+
script = new
|
78
|
+
string.split(' ').each do |v|
|
79
|
+
opcode = Opcodes.name_to_opcode(v)
|
80
|
+
if opcode
|
81
|
+
script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
|
82
|
+
else
|
83
|
+
script << v
|
84
|
+
end
|
85
|
+
end
|
86
|
+
script
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.parse_from_payload(payload)
|
90
|
+
s = new
|
91
|
+
buf = StringIO.new(payload)
|
92
|
+
until buf.eof?
|
93
|
+
opcode = buf.read(1)
|
94
|
+
if opcode.pushdata?
|
95
|
+
pushcode = opcode.ord
|
96
|
+
len = case pushcode
|
97
|
+
when OP_PUSHDATA1
|
98
|
+
buf.read(1).unpack('C').first
|
99
|
+
when OP_PUSHDATA2
|
100
|
+
buf.read(2).unpack('v').first
|
101
|
+
when OP_PUSHDATA4
|
102
|
+
buf.read(4).unpack('V').first
|
103
|
+
else
|
104
|
+
pushcode if pushcode < OP_PUSHDATA1
|
105
|
+
end
|
106
|
+
s << buf.read(len).bth if len
|
107
|
+
else
|
108
|
+
s << opcode.ord
|
109
|
+
end
|
110
|
+
end
|
111
|
+
s
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_payload
|
115
|
+
chunks.join
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_addr
|
119
|
+
return p2pkh_addr if p2pkh?
|
120
|
+
return p2wpkh_addr if p2wpkh?
|
121
|
+
return p2wsh_addr if p2wsh?
|
122
|
+
return p2sh_addr if p2sh?
|
123
|
+
end
|
124
|
+
|
125
|
+
# whether this script is a P2PKH format script.
|
126
|
+
def p2pkh?
|
127
|
+
return false unless chunks.size == 5
|
128
|
+
[OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
|
129
|
+
(chunks[0..1]+ chunks[3..4]).map(&:ord) && chunks[2].bytesize == 21
|
130
|
+
end
|
131
|
+
|
132
|
+
# whether this script is a P2WPKH format script.
|
133
|
+
def p2wpkh?
|
134
|
+
return false unless chunks.size == 2
|
135
|
+
chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 21
|
136
|
+
end
|
137
|
+
|
138
|
+
def p2wsh?
|
139
|
+
return false unless chunks.size == 2
|
140
|
+
chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 33
|
141
|
+
end
|
142
|
+
|
143
|
+
def p2sh?
|
144
|
+
return false unless chunks.size == 3
|
145
|
+
OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
|
146
|
+
end
|
147
|
+
|
148
|
+
# whether data push only script which dose not include other opcode
|
149
|
+
def data_only?
|
150
|
+
chunks.each do |c|
|
151
|
+
return false if !c.opcode.nil? && c.opcode > OP_16
|
152
|
+
end
|
153
|
+
true
|
154
|
+
end
|
155
|
+
|
156
|
+
# 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
|
+
def witness_program?
|
158
|
+
return false if size < 4 || size > 42 || chunks.size < 2
|
159
|
+
opcode = chunks[0].opcode
|
160
|
+
return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
|
161
|
+
return false unless chunks[1].pushdata?
|
162
|
+
program_size = chunks[1].pushed_data.bytesize
|
163
|
+
program_size >= 2 && program_size <= 40
|
164
|
+
end
|
165
|
+
|
166
|
+
# get witness version and witness program
|
167
|
+
def witness_data
|
168
|
+
version = opcode_to_small_int(chunks[0].opcode)
|
169
|
+
program = chunks[1].pushed_data
|
170
|
+
[version, program]
|
171
|
+
end
|
172
|
+
|
173
|
+
# append object to payload
|
174
|
+
def <<(obj)
|
175
|
+
if obj.is_a?(Integer)
|
176
|
+
push_int(obj)
|
177
|
+
elsif obj.is_a?(String)
|
178
|
+
append_data(obj.b)
|
179
|
+
elsif obj.is_a?(Array)
|
180
|
+
obj.each { |o| self.<< o}
|
181
|
+
self
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
# push integer to stack.
|
186
|
+
def push_int(n)
|
187
|
+
begin
|
188
|
+
append_opcode(n)
|
189
|
+
rescue ArgumentError
|
190
|
+
append_data(Script.encode_number(n))
|
191
|
+
end
|
192
|
+
self
|
193
|
+
end
|
194
|
+
|
195
|
+
# append opcode to payload
|
196
|
+
# @param [Integer] opcode append opcode which defined by Bitcoin::Opcodes
|
197
|
+
# @return [Script] return self
|
198
|
+
def append_opcode(opcode)
|
199
|
+
opcode = Opcodes.small_int_to_opcode(opcode) if -1 <= opcode && opcode <= 16
|
200
|
+
raise ArgumentError, "specified invalid opcode #{opcode}." unless Opcodes.defined?(opcode)
|
201
|
+
chunks << opcode.chr
|
202
|
+
self
|
203
|
+
end
|
204
|
+
|
205
|
+
# append data to payload with pushdata opcode
|
206
|
+
# @param [String] data append data. this data is not binary
|
207
|
+
# @return [Script] return self
|
208
|
+
def append_data(data)
|
209
|
+
chunks << Bitcoin::Script.pack_pushdata(data.htb)
|
210
|
+
self
|
211
|
+
end
|
212
|
+
|
213
|
+
def to_s
|
214
|
+
chunks.map { |c|
|
215
|
+
if c.pushdata?
|
216
|
+
v = Opcodes.opcode_to_small_int(c.ord)
|
217
|
+
v ? v : c.pushed_data.bth
|
218
|
+
else
|
219
|
+
Opcodes.opcode_to_name(c.ord)
|
220
|
+
end
|
221
|
+
}.join(' ')
|
222
|
+
end
|
223
|
+
|
224
|
+
# generate sha-256 hash for payload
|
225
|
+
def to_sha256
|
226
|
+
Bitcoin.sha256(to_payload).bth
|
227
|
+
end
|
228
|
+
|
229
|
+
# generate hash160 hash for payload
|
230
|
+
def to_hash160
|
231
|
+
Bitcoin.hash160(to_payload.bth)
|
232
|
+
end
|
233
|
+
|
234
|
+
# script size
|
235
|
+
def size
|
236
|
+
to_payload.bytesize
|
237
|
+
end
|
238
|
+
|
239
|
+
# encode int value to script number hex.
|
240
|
+
# The stacks hold byte vectors.
|
241
|
+
# When used as numbers, byte vectors are interpreted as little-endian variable-length integers
|
242
|
+
# with the most significant bit determining the sign of the integer.
|
243
|
+
# Thus 0x81 represents -1. 0x80 is another representation of zero (so called negative 0).
|
244
|
+
# Positive 0 is represented by a null-length vector.
|
245
|
+
# Byte vectors are interpreted as Booleans where False is represented by any representation of zero,
|
246
|
+
# and True is represented by any representation of non-zero.
|
247
|
+
def self.encode_number(i)
|
248
|
+
return '' if i == 0
|
249
|
+
negative = i < 0
|
250
|
+
|
251
|
+
hex = i.abs.to_s(16)
|
252
|
+
hex = '0' + hex unless (hex.length % 2).zero?
|
253
|
+
v = hex.htb.reverse # change endian
|
254
|
+
|
255
|
+
v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack('C').first & 0x80) == 0
|
256
|
+
v[-1] = [v[-1].unpack('C').first | 0x80].pack('C') if negative
|
257
|
+
v.bth
|
258
|
+
end
|
259
|
+
|
260
|
+
# decode script number hex to int value
|
261
|
+
def self.decode_number(s)
|
262
|
+
v = s.htb.reverse
|
263
|
+
return 0 if v.length.zero?
|
264
|
+
mbs = v[0].unpack('C').first
|
265
|
+
v[0] = [mbs - 0x80].pack('C') unless (mbs & 0x80) == 0
|
266
|
+
result = v.bth.to_i(16)
|
267
|
+
result = -result unless (mbs & 0x80) == 0
|
268
|
+
result
|
269
|
+
end
|
270
|
+
|
271
|
+
# binary +data+ convert pushdata which contains data length and append PUSHDATA opcode if necessary.
|
272
|
+
def self.pack_pushdata(data)
|
273
|
+
size = data.bytesize
|
274
|
+
header = if size < OP_PUSHDATA1
|
275
|
+
[size].pack('C')
|
276
|
+
elsif size < 0xff
|
277
|
+
[OP_PUSHDATA1, size].pack('CC')
|
278
|
+
elsif size < 0xffff
|
279
|
+
[OP_PUSHDATA2, size].pack('Cv')
|
280
|
+
elsif size < 0xffffffff
|
281
|
+
[OP_PUSHDATA4, size].pack('CV')
|
282
|
+
else
|
283
|
+
raise ArgumentError, 'data size is too big.'
|
284
|
+
end
|
285
|
+
header + data
|
286
|
+
end
|
287
|
+
|
288
|
+
# subscript this script to the specified range.
|
289
|
+
def subscript(*args)
|
290
|
+
s = self.class.new
|
291
|
+
s.chunks = chunks[*args]
|
292
|
+
s
|
293
|
+
end
|
294
|
+
|
295
|
+
# removes chunks matching subscript byte-for-byte and returns as a new object.
|
296
|
+
def find_and_delete(subscript)
|
297
|
+
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)
|
300
|
+
end
|
301
|
+
|
302
|
+
def ==(other)
|
303
|
+
return false unless other
|
304
|
+
chunks == other.chunks
|
305
|
+
end
|
306
|
+
|
307
|
+
private
|
308
|
+
|
309
|
+
# generate p2pkh address. if script dose not p2pkh, return nil.
|
310
|
+
def p2pkh_addr
|
311
|
+
return nil unless p2pkh?
|
312
|
+
hash160 = chunks[2].pushed_data.bth
|
313
|
+
return nil unless hash160.htb.bytesize == 20
|
314
|
+
hex = Bitcoin.chain_params.address_version + hash160
|
315
|
+
Bitcoin.encode_base58_address(hex)
|
316
|
+
end
|
317
|
+
|
318
|
+
# generate p2wpkh address. if script dose not p2wpkh, return nil.
|
319
|
+
def p2wpkh_addr
|
320
|
+
p2wpkh? ? bech32_addr : nil
|
321
|
+
end
|
322
|
+
|
323
|
+
# generate p2sh address. if script dose not p2sh, return nil.
|
324
|
+
def p2sh_addr
|
325
|
+
return nil unless p2sh?
|
326
|
+
hash160 = chunks[1].pushed_data.bth
|
327
|
+
return nil unless hash160.htb.bytesize == 20
|
328
|
+
hex = Bitcoin.chain_params.p2sh_version + hash160
|
329
|
+
Bitcoin.encode_base58_address(hex)
|
330
|
+
end
|
331
|
+
|
332
|
+
# generate p2wsh address. if script dose not p2wsh, return nil.
|
333
|
+
def p2wsh_addr
|
334
|
+
p2wsh? ? bech32_addr : nil
|
335
|
+
end
|
336
|
+
|
337
|
+
# return bech32 address for payload
|
338
|
+
def bech32_addr
|
339
|
+
segwit_addr = Bech32::SegwitAddr.new
|
340
|
+
segwit_addr.hrp = Bitcoin.chain_params.bech32_hrp
|
341
|
+
segwit_addr.script_pubkey = to_payload.bth
|
342
|
+
segwit_addr.addr
|
343
|
+
end
|
344
|
+
|
345
|
+
end
|
346
|
+
|
347
|
+
end
|