bitcoinrb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|