bitcoin-ruby 0.0.1 → 0.0.2
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.
- data/.gitignore +4 -1
- data/Gemfile +21 -0
- data/README.rdoc +85 -25
- data/Rakefile +7 -3
- data/bin/bitcoin_node +39 -42
- data/bin/bitcoin_shell +1 -0
- data/bin/bitcoin_wallet +129 -53
- data/bitcoin-ruby.gemspec +4 -7
- data/concept-examples/blockchain-pow.rb +1 -1
- data/doc/CONFIG.rdoc +5 -5
- data/doc/EXAMPLES.rdoc +9 -5
- data/doc/NAMECOIN.rdoc +34 -0
- data/doc/NODE.rdoc +147 -10
- data/examples/balance.rb +10 -4
- data/examples/bbe_verify_tx.rb +7 -2
- data/examples/forwarder.rb +73 -0
- data/examples/generate_tx.rb +34 -0
- data/examples/simple_network_monitor_and_util.rb +187 -0
- data/examples/verify_tx.rb +1 -1
- data/lib/bitcoin.rb +308 -18
- data/lib/bitcoin/builder.rb +62 -36
- data/lib/bitcoin/config.rb +2 -0
- data/lib/bitcoin/connection.rb +11 -8
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ffi/openssl.rb +187 -21
- data/lib/bitcoin/gui/addr_view.rb +2 -0
- data/lib/bitcoin/gui/conn_view.rb +2 -0
- data/lib/bitcoin/gui/connection.rb +2 -0
- data/lib/bitcoin/gui/em_gtk.rb +2 -0
- data/lib/bitcoin/gui/gui.rb +2 -0
- data/lib/bitcoin/gui/helpers.rb +2 -0
- data/lib/bitcoin/gui/tree_view.rb +2 -0
- data/lib/bitcoin/gui/tx_view.rb +2 -0
- data/lib/bitcoin/key.rb +77 -11
- data/lib/bitcoin/litecoin.rb +81 -0
- data/lib/bitcoin/logger.rb +20 -1
- data/lib/bitcoin/namecoin.rb +279 -0
- data/lib/bitcoin/network/command_client.rb +7 -6
- data/lib/bitcoin/network/command_handler.rb +229 -43
- data/lib/bitcoin/network/connection_handler.rb +182 -70
- data/lib/bitcoin/network/node.rb +231 -106
- data/lib/bitcoin/protocol.rb +44 -23
- data/lib/bitcoin/protocol/address.rb +5 -3
- data/lib/bitcoin/protocol/alert.rb +3 -4
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +98 -18
- data/lib/bitcoin/protocol/handler.rb +6 -5
- data/lib/bitcoin/protocol/parser.rb +44 -19
- data/lib/bitcoin/protocol/tx.rb +105 -52
- data/lib/bitcoin/protocol/txin.rb +39 -19
- data/lib/bitcoin/protocol/txout.rb +28 -13
- data/lib/bitcoin/protocol/version.rb +16 -7
- data/lib/bitcoin/script.rb +579 -122
- data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
- data/lib/bitcoin/storage/models.rb +20 -7
- data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
- data/lib/bitcoin/storage/storage.rb +233 -28
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
- data/lib/bitcoin/validation.rb +369 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/coinselector.rb +3 -0
- data/lib/bitcoin/wallet/keygenerator.rb +3 -1
- data/lib/bitcoin/wallet/keystore.rb +6 -2
- data/lib/bitcoin/wallet/txdp.rb +6 -4
- data/lib/bitcoin/wallet/wallet.rb +54 -16
- data/spec/bitcoin/bitcoin_spec.rb +48 -3
- data/spec/bitcoin/builder_spec.rb +40 -17
- data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
- data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
- data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
- data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
- data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
- data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
- data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
- data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
- data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
- data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
- data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
- data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
- data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
- data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
- data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
- data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
- data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
- data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
- data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
- data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
- data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
- data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
- data/spec/bitcoin/key_spec.rb +128 -3
- data/spec/bitcoin/namecoin_spec.rb +182 -0
- data/spec/bitcoin/network_spec.rb +5 -3
- data/spec/bitcoin/node/command_api_spec.rb +376 -0
- data/spec/bitcoin/protocol/addr_spec.rb +2 -0
- data/spec/bitcoin/protocol/alert_spec.rb +2 -0
- data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
- data/spec/bitcoin/protocol/block_spec.rb +134 -39
- data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -8
- data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
- data/spec/bitcoin/protocol/ping_spec.rb +2 -0
- data/spec/bitcoin/protocol/tx_spec.rb +83 -17
- data/spec/bitcoin/protocol/version_spec.rb +7 -5
- data/spec/bitcoin/script/opcodes_spec.rb +412 -133
- data/spec/bitcoin/script/script_spec.rb +112 -13
- data/spec/bitcoin/spec_helper.rb +68 -0
- data/spec/bitcoin/storage/reorg_spec.rb +199 -0
- data/spec/bitcoin/storage/storage_spec.rb +337 -0
- data/spec/bitcoin/storage/validation_spec.rb +261 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
- data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
- metadata +105 -51
- data/lib/bitcoin/storage/sequel.rb +0 -335
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
- data/spec/bitcoin/reorg_spec.rb +0 -129
- data/spec/bitcoin/storage_spec.rb +0 -229
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
module Bitcoin
|
|
2
4
|
module Protocol
|
|
3
5
|
|
|
@@ -23,26 +25,38 @@ module Bitcoin
|
|
|
23
25
|
|
|
24
26
|
# parse raw binary data for transaction output
|
|
25
27
|
def parse_data(data)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
buf = data.is_a?(String) ? StringIO.new(data) : data
|
|
29
|
+
parse_data_from_io(buf)
|
|
30
|
+
buf.pos
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.from_io(buf)
|
|
34
|
+
txout = new; txout.parse_data_from_io(buf); txout
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# parse raw binary data for transaction output
|
|
38
|
+
def parse_data_from_io(buf)
|
|
39
|
+
@value = buf.read(8).unpack("Q")[0]
|
|
40
|
+
@pk_script_length = Protocol.unpack_var_int_from_io(buf)
|
|
41
|
+
@pk_script = buf.read(@pk_script_length)
|
|
32
42
|
end
|
|
33
43
|
|
|
34
44
|
alias :parse_payload :parse_data
|
|
35
45
|
|
|
36
46
|
def to_payload
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
[@value].pack("Q") << Protocol.pack_var_int(@pk_script_length) << @pk_script
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_null_payload
|
|
51
|
+
self.class.new(-1, '').to_payload
|
|
41
52
|
end
|
|
42
53
|
|
|
43
|
-
def to_hash
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
def to_hash(options = {})
|
|
55
|
+
script = Bitcoin::Script.new(@pk_script)
|
|
56
|
+
h = { 'value' => "%.8f" % (@value / 100000000.0),
|
|
57
|
+
'scriptPubKey' => script.to_string }
|
|
58
|
+
h["address"] = script.get_address if script.is_hash160? && options[:with_address]
|
|
59
|
+
h
|
|
46
60
|
end
|
|
47
61
|
|
|
48
62
|
def self.from_hash(output)
|
|
@@ -64,6 +78,7 @@ module Bitcoin
|
|
|
64
78
|
# create output spending +value+ btc (base units) to +address+
|
|
65
79
|
def self.value_to_address(value, address)
|
|
66
80
|
pk_script = Bitcoin::Script.to_address_script(address)
|
|
81
|
+
raise "Script#pk_script nil with address #{address}" unless pk_script
|
|
67
82
|
new(value, pk_script)
|
|
68
83
|
end
|
|
69
84
|
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
module Bitcoin
|
|
2
4
|
module Protocol
|
|
3
5
|
|
|
6
|
+
# https://en.bitcoin.it/wiki/Protocol_specification#version
|
|
4
7
|
class Version
|
|
8
|
+
# services bit constants
|
|
9
|
+
NODE_NETWORK = (1 << 0)
|
|
10
|
+
|
|
5
11
|
attr_reader :fields
|
|
12
|
+
|
|
6
13
|
def initialize(opts={})
|
|
7
14
|
@fields = {
|
|
8
|
-
:version => Bitcoin
|
|
9
|
-
:services =>
|
|
15
|
+
:version => Bitcoin.network[:protocol_version],
|
|
16
|
+
:services => NODE_NETWORK,
|
|
10
17
|
:time => Time.now.tv_sec,
|
|
11
18
|
:from => "127.0.0.1:8333",
|
|
12
19
|
:to => "127.0.0.1:8333",
|
|
@@ -18,12 +25,12 @@ module Bitcoin
|
|
|
18
25
|
|
|
19
26
|
def to_payload
|
|
20
27
|
payload = [
|
|
21
|
-
@fields.values_at(:version, :services, :time).pack("
|
|
28
|
+
@fields.values_at(:version, :services, :time).pack("VQQ"),
|
|
22
29
|
pack_address_field(@fields[:from]),
|
|
23
30
|
pack_address_field(@fields[:to]),
|
|
24
31
|
@fields.values_at(:nonce).pack("Q"),
|
|
25
32
|
Protocol.pack_var_string(@fields[:user_agent]),
|
|
26
|
-
@fields.values_at(:last_block).pack("
|
|
33
|
+
@fields.values_at(:last_block).pack("V")
|
|
27
34
|
].join
|
|
28
35
|
end
|
|
29
36
|
|
|
@@ -32,15 +39,15 @@ module Bitcoin
|
|
|
32
39
|
end
|
|
33
40
|
|
|
34
41
|
def parse(payload)
|
|
35
|
-
version, services, timestamp, to, from, nonce, payload = payload.unpack("
|
|
42
|
+
version, services, timestamp, to, from, nonce, payload = payload.unpack("VQQa26a26Qa*")
|
|
36
43
|
to, from = unpack_address_field(to), unpack_address_field(from)
|
|
37
44
|
user_agent, payload = Protocol.unpack_var_string(payload)
|
|
38
|
-
last_block = payload.unpack("
|
|
45
|
+
last_block = payload.unpack("V")[0]
|
|
39
46
|
|
|
40
47
|
@fields = {
|
|
41
48
|
:version => version, :services => services, :time => timestamp,
|
|
42
49
|
:from => from, :to => to, :nonce => nonce,
|
|
43
|
-
:user_agent => user_agent, :last_block => last_block
|
|
50
|
+
:user_agent => user_agent.to_s, :last_block => last_block
|
|
44
51
|
}
|
|
45
52
|
self
|
|
46
53
|
end
|
|
@@ -63,6 +70,8 @@ module Bitcoin
|
|
|
63
70
|
@fields[:time] - Time.now.tv_sec
|
|
64
71
|
end
|
|
65
72
|
|
|
73
|
+
def method_missing(*a); (@fields[a.first] rescue nil) or super(*a); end
|
|
74
|
+
|
|
66
75
|
def self.parse(payload); new.parse(payload); end
|
|
67
76
|
end
|
|
68
77
|
|
data/lib/bitcoin/script.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
require 'bitcoin'
|
|
2
4
|
|
|
3
5
|
class Bitcoin::Script
|
|
@@ -6,9 +8,11 @@ class Bitcoin::Script
|
|
|
6
8
|
OP_TRUE = 81
|
|
7
9
|
OP_0 = 0
|
|
8
10
|
OP_FALSE = 0
|
|
11
|
+
OP_PUSHDATA0 = 0
|
|
9
12
|
OP_PUSHDATA1 = 76
|
|
10
13
|
OP_PUSHDATA2 = 77
|
|
11
14
|
OP_PUSHDATA4 = 78
|
|
15
|
+
OP_PUSHDATA_INVALID = 238 # 0xEE
|
|
12
16
|
OP_NOP = 97
|
|
13
17
|
OP_DUP = 118
|
|
14
18
|
OP_HASH160 = 169
|
|
@@ -32,9 +36,16 @@ class Bitcoin::Script
|
|
|
32
36
|
OP_SHA256 = 168
|
|
33
37
|
OP_SHA1 = 167
|
|
34
38
|
OP_RIPEMD160 = 166
|
|
35
|
-
|
|
39
|
+
OP_NOP1 = 176
|
|
36
40
|
OP_NOP2 = 177
|
|
37
|
-
|
|
41
|
+
OP_NOP3 = 178
|
|
42
|
+
OP_NOP4 = 179
|
|
43
|
+
OP_NOP5 = 180
|
|
44
|
+
OP_NOP6 = 181
|
|
45
|
+
OP_NOP7 = 182
|
|
46
|
+
OP_NOP8 = 183
|
|
47
|
+
OP_NOP9 = 184
|
|
48
|
+
OP_NOP10 = 185
|
|
38
49
|
OP_CODESEPARATOR = 171
|
|
39
50
|
OP_MIN = 163
|
|
40
51
|
OP_MAX = 164
|
|
@@ -43,10 +54,52 @@ class Bitcoin::Script
|
|
|
43
54
|
OP_IFDUP = 115
|
|
44
55
|
OP_DEPTH = 116
|
|
45
56
|
OP_1NEGATE = 79
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
OP_WITHIN = 165
|
|
58
|
+
OP_NUMEQUAL = 156
|
|
59
|
+
OP_NUMEQUALVERIFY = 157
|
|
60
|
+
OP_LESSTHAN = 159
|
|
61
|
+
OP_LESSTHANOREQUAL = 161
|
|
62
|
+
OP_GREATERTHAN = 160
|
|
63
|
+
OP_NOT = 145
|
|
64
|
+
OP_0NOTEQUAL = 146
|
|
65
|
+
OP_ABS = 144
|
|
66
|
+
OP_1ADD = 139
|
|
67
|
+
OP_1SUB = 140
|
|
68
|
+
OP_NEGATE = 143
|
|
69
|
+
OP_BOOLOR = 155
|
|
70
|
+
OP_NUMNOTEQUAL = 158
|
|
71
|
+
OP_RETURN = 106
|
|
72
|
+
OP_OVER = 120
|
|
73
|
+
OP_IF = 99
|
|
74
|
+
OP_NOTIF = 100
|
|
75
|
+
OP_ELSE = 103
|
|
76
|
+
OP_ENDIF = 104
|
|
77
|
+
OP_PICK = 121
|
|
78
|
+
OP_SIZE = 130
|
|
79
|
+
OP_VER = 98
|
|
80
|
+
OP_ROLL = 122
|
|
81
|
+
OP_ROT = 123
|
|
82
|
+
OP_2DROP = 109
|
|
83
|
+
OP_2DUP = 110
|
|
84
|
+
OP_3DUP = 111
|
|
85
|
+
OP_NIP = 119
|
|
86
|
+
|
|
87
|
+
OP_CAT = 126
|
|
88
|
+
OP_SUBSTR = 127
|
|
89
|
+
OP_LEFT = 128
|
|
90
|
+
OP_RIGHT = 129
|
|
91
|
+
OP_INVERT = 131
|
|
92
|
+
OP_AND = 132
|
|
93
|
+
OP_OR = 133
|
|
94
|
+
OP_XOR = 134
|
|
95
|
+
OP_2MUL = 141
|
|
96
|
+
OP_2DIV = 142
|
|
97
|
+
OP_MUL = 149
|
|
98
|
+
OP_DIV = 150
|
|
99
|
+
OP_MOD = 151
|
|
100
|
+
OP_LSHIFT = 152
|
|
101
|
+
OP_RSHIFT = 153
|
|
102
|
+
|
|
50
103
|
|
|
51
104
|
OPCODES = Hash[*constants.grep(/^OP_/).map{|i| [const_get(i), i.to_s] }.flatten]
|
|
52
105
|
OPCODES[0] = "0"
|
|
@@ -55,19 +108,44 @@ class Bitcoin::Script
|
|
|
55
108
|
OPCODES_ALIAS = {
|
|
56
109
|
"OP_TRUE" => OP_1,
|
|
57
110
|
"OP_FALSE" => OP_0,
|
|
58
|
-
"
|
|
59
|
-
"
|
|
111
|
+
"OP_EVAL" => OP_NOP1,
|
|
112
|
+
"OP_CHECKHASHVERIFY" => OP_NOP2,
|
|
60
113
|
}
|
|
61
114
|
|
|
115
|
+
DISABLED_OPCODES = [
|
|
116
|
+
OP_CAT, OP_SUBSTR, OP_LEFT, OP_RIGHT, OP_INVERT,
|
|
117
|
+
OP_AND, OP_OR, OP_XOR, OP_2MUL, OP_2DIV, OP_MUL,
|
|
118
|
+
OP_DIV, OP_MOD, OP_LSHIFT, OP_RSHIFT
|
|
119
|
+
]
|
|
120
|
+
|
|
62
121
|
OP_2_16 = (82..96).to_a
|
|
63
122
|
|
|
123
|
+
|
|
124
|
+
OPCODES_PARSE_BINARY = {}
|
|
125
|
+
OPCODES.each{|k,v| OPCODES_PARSE_BINARY[k] = v }
|
|
126
|
+
OP_2_16.each{|i| OPCODES_PARSE_BINARY[i] = (OP_2_16.index(i)+2).to_s }
|
|
127
|
+
|
|
128
|
+
OPCODES_PARSE_STRING = {}
|
|
129
|
+
OPCODES.each{|k,v| OPCODES_PARSE_STRING[v] = k }
|
|
130
|
+
OPCODES_ALIAS.each{|k,v| OPCODES_PARSE_STRING[k] = v }
|
|
131
|
+
2.upto(16).each{|i| OPCODES_PARSE_STRING["OP_#{i}"] = OP_2_16[i-2] }
|
|
132
|
+
2.upto(16).each{|i| OPCODES_PARSE_STRING["#{i}" ] = OP_2_16[i-2] }
|
|
133
|
+
[1,2,4].each{|i| OPCODES_PARSE_STRING.delete("OP_PUSHDATA#{i}") }
|
|
134
|
+
|
|
135
|
+
|
|
64
136
|
attr_reader :raw, :chunks, :debug
|
|
65
137
|
|
|
66
138
|
# create a new script. +bytes+ is typically input_script + output_script
|
|
67
139
|
def initialize(bytes, offset=0)
|
|
68
140
|
@raw = bytes
|
|
69
|
-
@stack, @stack_alt = [], []
|
|
141
|
+
@stack, @stack_alt, @exec_stack = [], [], []
|
|
70
142
|
@chunks = parse(bytes, offset)
|
|
143
|
+
@do_exec = true
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
class ::String
|
|
147
|
+
attr_accessor :bitcoin_pushdata
|
|
148
|
+
attr_accessor :bitcoin_pushdata_length
|
|
71
149
|
end
|
|
72
150
|
|
|
73
151
|
# parse raw script
|
|
@@ -76,43 +154,135 @@ class Bitcoin::Script
|
|
|
76
154
|
chunks = []
|
|
77
155
|
until program.empty?
|
|
78
156
|
opcode = program.shift(1)[0]
|
|
79
|
-
if opcode >= 0xf0
|
|
80
|
-
opcode = (opcode << 8) | program.shift(1)[0]
|
|
81
|
-
end
|
|
82
157
|
|
|
83
158
|
if (opcode > 0) && (opcode < OP_PUSHDATA1)
|
|
84
|
-
len = opcode
|
|
159
|
+
len, tmp = opcode, program[0]
|
|
85
160
|
chunks << program.shift(len).pack("C*")
|
|
161
|
+
|
|
162
|
+
# 0x16 = 22 due to OP_2_16 from_string parsing
|
|
163
|
+
if len == 1 && tmp <= 22
|
|
164
|
+
chunks.last.bitcoin_pushdata = OP_PUSHDATA0
|
|
165
|
+
chunks.last.bitcoin_pushdata_length = len
|
|
166
|
+
else
|
|
167
|
+
raise "invalid OP_PUSHDATA0" if len != chunks.last.bytesize
|
|
168
|
+
end
|
|
86
169
|
elsif (opcode == OP_PUSHDATA1)
|
|
87
170
|
len = program.shift(1)[0]
|
|
88
171
|
chunks << program.shift(len).pack("C*")
|
|
172
|
+
|
|
173
|
+
unless len > OP_PUSHDATA1 && len <= 0xff
|
|
174
|
+
chunks.last.bitcoin_pushdata = OP_PUSHDATA1
|
|
175
|
+
chunks.last.bitcoin_pushdata_length = len
|
|
176
|
+
else
|
|
177
|
+
raise "invalid OP_PUSHDATA1" if len != chunks.last.bytesize
|
|
178
|
+
end
|
|
89
179
|
elsif (opcode == OP_PUSHDATA2)
|
|
90
|
-
len = program.shift(2).pack("C*").unpack("
|
|
180
|
+
len = program.shift(2).pack("C*").unpack("v")[0]
|
|
91
181
|
chunks << program.shift(len).pack("C*")
|
|
182
|
+
|
|
183
|
+
unless len > 0xff && len <= 0xffff
|
|
184
|
+
chunks.last.bitcoin_pushdata = OP_PUSHDATA2
|
|
185
|
+
chunks.last.bitcoin_pushdata_length = len
|
|
186
|
+
else
|
|
187
|
+
raise "invalid OP_PUSHDATA2" if len != chunks.last.bytesize
|
|
188
|
+
end
|
|
92
189
|
elsif (opcode == OP_PUSHDATA4)
|
|
93
|
-
len = program.shift(4).pack("C*").unpack("
|
|
190
|
+
len = program.shift(4).pack("C*").unpack("V")[0]
|
|
94
191
|
chunks << program.shift(len).pack("C*")
|
|
192
|
+
|
|
193
|
+
unless len > 0xffff # && len <= 0xffffffff
|
|
194
|
+
chunks.last.bitcoin_pushdata = OP_PUSHDATA4
|
|
195
|
+
chunks.last.bitcoin_pushdata_length = len
|
|
196
|
+
else
|
|
197
|
+
raise "invalid OP_PUSHDATA4" if len != chunks.last.bytesize
|
|
198
|
+
end
|
|
95
199
|
else
|
|
96
200
|
chunks << opcode
|
|
97
201
|
end
|
|
98
202
|
end
|
|
99
203
|
chunks
|
|
204
|
+
rescue Exception => ex
|
|
205
|
+
# bail out! #run returns false but serialization roundtrips still create the right payload.
|
|
206
|
+
@parse_invalid = true
|
|
207
|
+
c = bytes.unpack("C*").pack("C*")
|
|
208
|
+
c.bitcoin_pushdata = OP_PUSHDATA_INVALID
|
|
209
|
+
c.bitcoin_pushdata_length = c.bytesize
|
|
210
|
+
chunks = [ c ]
|
|
100
211
|
end
|
|
101
212
|
|
|
102
213
|
# string representation of the script
|
|
103
214
|
def to_string(chunks=nil)
|
|
104
|
-
|
|
105
|
-
|
|
215
|
+
string = ""
|
|
216
|
+
(chunks || @chunks).each.with_index{|i,idx|
|
|
217
|
+
string << " " unless idx == 0
|
|
218
|
+
string << case i
|
|
106
219
|
when Fixnum
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
220
|
+
if opcode = OPCODES_PARSE_BINARY[i]
|
|
221
|
+
opcode
|
|
222
|
+
else
|
|
223
|
+
"(opcode-#{i})"
|
|
111
224
|
end
|
|
112
225
|
when String
|
|
113
|
-
i.
|
|
226
|
+
if i.bitcoin_pushdata
|
|
227
|
+
"#{i.bitcoin_pushdata}:#{i.bitcoin_pushdata_length}:".force_encoding('binary') + i.unpack("H*")[0]
|
|
228
|
+
else
|
|
229
|
+
i.unpack("H*")[0]
|
|
230
|
+
end
|
|
114
231
|
end
|
|
115
|
-
}
|
|
232
|
+
}
|
|
233
|
+
string
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def to_binary(chunks=nil)
|
|
237
|
+
(chunks || @chunks).map{|chunk|
|
|
238
|
+
case chunk
|
|
239
|
+
when Fixnum; [chunk].pack("C*")
|
|
240
|
+
when String; self.class.pack_pushdata(chunk)
|
|
241
|
+
end
|
|
242
|
+
}.join
|
|
243
|
+
end
|
|
244
|
+
alias :to_payload :to_binary
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def to_binary_without_signatures(drop_signatures, chunks=nil)
|
|
248
|
+
to_binary( (chunks || @chunks).select{|i| drop_signatures.none?{|e| e == i } } )
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def self.pack_pushdata(data)
|
|
253
|
+
size = data.bytesize
|
|
254
|
+
|
|
255
|
+
if data.bitcoin_pushdata
|
|
256
|
+
size = data.bitcoin_pushdata_length
|
|
257
|
+
pack_pushdata_align(data.bitcoin_pushdata, size, data)
|
|
258
|
+
else
|
|
259
|
+
head = if size < OP_PUSHDATA1
|
|
260
|
+
[size].pack("C")
|
|
261
|
+
elsif size <= 0xff
|
|
262
|
+
[OP_PUSHDATA1, size].pack("CC")
|
|
263
|
+
elsif size <= 0xffff
|
|
264
|
+
[OP_PUSHDATA2, size].pack("Cv")
|
|
265
|
+
#elsif size <= 0xffffffff
|
|
266
|
+
else
|
|
267
|
+
[OP_PUSHDATA4, size].pack("CV")
|
|
268
|
+
end
|
|
269
|
+
head + data
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def self.pack_pushdata_align(pushdata, len, data)
|
|
274
|
+
case pushdata
|
|
275
|
+
when OP_PUSHDATA1
|
|
276
|
+
[OP_PUSHDATA1, len].pack("CC") + data
|
|
277
|
+
when OP_PUSHDATA2
|
|
278
|
+
[OP_PUSHDATA2, len].pack("Cv") + data
|
|
279
|
+
when OP_PUSHDATA4
|
|
280
|
+
[OP_PUSHDATA4, len].pack("CV") + data
|
|
281
|
+
when OP_PUSHDATA_INVALID
|
|
282
|
+
data
|
|
283
|
+
else # OP_PUSHDATA0
|
|
284
|
+
[len].pack("C") + data
|
|
285
|
+
end
|
|
116
286
|
end
|
|
117
287
|
|
|
118
288
|
# script object of a string representation
|
|
@@ -124,33 +294,33 @@ class Bitcoin::Script
|
|
|
124
294
|
|
|
125
295
|
# raw script binary of a string representation
|
|
126
296
|
def self.binary_from_string(script_string)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
elsif size > 0xffff && size <= 0xffffffff
|
|
146
|
-
[OP_PUSHDATA4, size].pack("CV")
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
head + data
|
|
297
|
+
buf = ""
|
|
298
|
+
script_string.split(" ").each{|i|
|
|
299
|
+
i = if opcode = OPCODES_PARSE_STRING[i]
|
|
300
|
+
opcode
|
|
301
|
+
else
|
|
302
|
+
case i
|
|
303
|
+
when /OP_PUSHDATA/ # skip
|
|
304
|
+
when /OP_(.+)$/; raise ScriptOpcodeError, "#{i} not defined!"
|
|
305
|
+
when /\(opcode\-(\d+)\)/; $1.to_i
|
|
306
|
+
when "(opcode"; # skip # fix invalid opcode parsing
|
|
307
|
+
when /^(\d+)\)/; $1.to_i # fix invalid opcode parsing
|
|
308
|
+
when /(\d+):(\d+):(.+)?/
|
|
309
|
+
pushdata, len, data = $1.to_i, $2.to_i, $3
|
|
310
|
+
pack_pushdata_align(pushdata, len, [data].pack("H*"))
|
|
311
|
+
else
|
|
312
|
+
data = [i].pack("H*")
|
|
313
|
+
pack_pushdata(data)
|
|
314
|
+
end
|
|
150
315
|
end
|
|
151
|
-
|
|
152
|
-
i.is_a?(Fixnum)
|
|
153
|
-
|
|
316
|
+
|
|
317
|
+
buf << if i.is_a?(Fixnum)
|
|
318
|
+
i < 256 ? [i].pack("C") : [OpenSSL::BN.new(i.to_s,10).to_hex].pack("H*")
|
|
319
|
+
else
|
|
320
|
+
i
|
|
321
|
+
end if i
|
|
322
|
+
}
|
|
323
|
+
buf
|
|
154
324
|
end
|
|
155
325
|
|
|
156
326
|
def invalid?
|
|
@@ -158,35 +328,55 @@ class Bitcoin::Script
|
|
|
158
328
|
end
|
|
159
329
|
|
|
160
330
|
# run the script. +check_callback+ is called for OP_CHECKSIG operations
|
|
161
|
-
def run(&check_callback)
|
|
162
|
-
return
|
|
331
|
+
def run(block_timestamp=Time.now.to_i, &check_callback)
|
|
332
|
+
return false if @parse_invalid
|
|
333
|
+
|
|
334
|
+
#p [to_string, block_timestamp, is_p2sh?]
|
|
335
|
+
@script_invalid = true if @raw.bytesize > 10_000
|
|
336
|
+
|
|
337
|
+
if block_timestamp >= 1333238400 # Pay to Script Hash (BIP 0016)
|
|
338
|
+
return pay_to_script_hash(check_callback) if is_p2sh?
|
|
339
|
+
end
|
|
340
|
+
|
|
163
341
|
@debug = []
|
|
164
342
|
@chunks.each{|chunk|
|
|
165
343
|
break if invalid?
|
|
344
|
+
|
|
166
345
|
@debug << @stack.map{|i| i.unpack("H*") rescue i}
|
|
167
|
-
|
|
346
|
+
@do_exec = @exec_stack.count(false) == 0 ? true : false
|
|
347
|
+
#p [@stack, @do_exec]
|
|
348
|
+
|
|
168
349
|
case chunk
|
|
169
350
|
when Fixnum
|
|
170
|
-
|
|
351
|
+
if DISABLED_OPCODES.include?(chunk)
|
|
352
|
+
@script_invalid = true
|
|
353
|
+
@debug << "DISABLED_#{OPCODES[chunk]}"
|
|
354
|
+
break
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
next unless (@do_exec || (OP_IF <= chunk && chunk <= OP_ENDIF))
|
|
171
358
|
|
|
359
|
+
case chunk
|
|
172
360
|
when *OPCODES_METHOD.keys
|
|
173
361
|
m = method( n=OPCODES_METHOD[chunk] )
|
|
174
362
|
@debug << n.to_s.upcase
|
|
175
363
|
(m.arity == 1) ? m.call(check_callback) : m.call # invoke opcode method
|
|
176
|
-
|
|
177
364
|
when *OP_2_16
|
|
178
365
|
@stack << OP_2_16.index(chunk) + 2
|
|
179
366
|
@debug << "OP_#{chunk-80}"
|
|
180
367
|
else
|
|
181
368
|
name = OPCODES[chunk] || chunk
|
|
369
|
+
puts "Bitcoin::Script: opcode #{name} unkown or not implemented\n#{to_string.inspect}"
|
|
182
370
|
raise "opcode #{name} unkown or not implemented"
|
|
183
371
|
end
|
|
184
372
|
when String
|
|
185
|
-
@
|
|
186
|
-
|
|
373
|
+
if @do_exec
|
|
374
|
+
@debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
|
|
375
|
+
@stack << chunk
|
|
376
|
+
end
|
|
187
377
|
end
|
|
188
378
|
}
|
|
189
|
-
@debug << @stack.map{|i| i.unpack("H*") rescue i }
|
|
379
|
+
@debug << @stack.map{|i| i.unpack("H*") rescue i } #if @do_exec
|
|
190
380
|
|
|
191
381
|
if @script_invalid
|
|
192
382
|
@stack << 0
|
|
@@ -195,7 +385,7 @@ class Bitcoin::Script
|
|
|
195
385
|
|
|
196
386
|
@debug << "RESULT"
|
|
197
387
|
return false if @stack.empty?
|
|
198
|
-
return false if @stack.pop
|
|
388
|
+
return false if [0, ''].include?(@stack.pop)
|
|
199
389
|
true
|
|
200
390
|
end
|
|
201
391
|
|
|
@@ -203,13 +393,6 @@ class Bitcoin::Script
|
|
|
203
393
|
@script_invalid = true; nil
|
|
204
394
|
end
|
|
205
395
|
|
|
206
|
-
def codehash_script(opcode)
|
|
207
|
-
# CScript scriptCode(pbegincodehash, pend);
|
|
208
|
-
script = to_string(@chunks[(@codehash_start||0)...@chunks.size-@chunks.reverse.index(opcode)])
|
|
209
|
-
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
|
210
|
-
[script, checkhash]
|
|
211
|
-
end
|
|
212
|
-
|
|
213
396
|
def self.drop_signatures(script_pubkey, drop_signatures)
|
|
214
397
|
script = new(script_pubkey).to_string.split(" ").delete_if{|c| drop_signatures.include?(c) }.join(" ")
|
|
215
398
|
script_pubkey = binary_from_string(script)
|
|
@@ -219,17 +402,24 @@ class Bitcoin::Script
|
|
|
219
402
|
#
|
|
220
403
|
# <sig> {<pub> OP_CHECKSIG} | OP_HASH160 <script_hash> OP_EQUAL
|
|
221
404
|
def pay_to_script_hash(check_callback)
|
|
222
|
-
return false
|
|
223
|
-
script_hash = @chunks
|
|
224
|
-
script =
|
|
225
|
-
sig = self.class.from_string(@chunks[0].unpack("H*")[0]).raw
|
|
405
|
+
return false if @chunks.size < 4
|
|
406
|
+
*rest, script, _, script_hash, _ = @chunks
|
|
407
|
+
script, script_hash = cast_to_string(script), cast_to_string(script_hash)
|
|
226
408
|
|
|
227
409
|
return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
|
|
228
|
-
|
|
229
|
-
|
|
410
|
+
rest.delete_at(0) if rest[0] && cast_to_bignum(rest[0]) == 0
|
|
411
|
+
|
|
412
|
+
script = self.class.new(to_binary(rest) + script).inner_p2sh!(script)
|
|
413
|
+
result = script.run(&check_callback)
|
|
414
|
+
@debug = script.debug
|
|
415
|
+
result
|
|
230
416
|
end
|
|
231
417
|
|
|
418
|
+
def inner_p2sh!(script=nil); @inner_p2sh = true; @inner_script_code = script; self; end
|
|
419
|
+
def inner_p2sh?; @inner_p2sh; end
|
|
420
|
+
|
|
232
421
|
def is_pay_to_script_hash?
|
|
422
|
+
return false unless @chunks[-2].is_a?(String)
|
|
233
423
|
@chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
|
|
234
424
|
@chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
|
|
235
425
|
end
|
|
@@ -258,15 +448,16 @@ class Bitcoin::Script
|
|
|
258
448
|
# is this a multisig tx
|
|
259
449
|
def is_multisig?
|
|
260
450
|
return false if @chunks.size > 6 || @chunks.size < 4
|
|
261
|
-
@chunks[-1] == OP_CHECKMULTISIG
|
|
451
|
+
@chunks[-1] == OP_CHECKMULTISIG and get_multisig_pubkeys.all?{|c| c.is_a?(String) }
|
|
262
452
|
end
|
|
263
453
|
|
|
454
|
+
# get type of this tx
|
|
264
455
|
def type
|
|
265
|
-
|
|
266
|
-
elsif is_pubkey?;
|
|
267
|
-
elsif is_multisig?;
|
|
268
|
-
elsif is_p2sh?;
|
|
269
|
-
else;
|
|
456
|
+
if is_hash160?; :hash160
|
|
457
|
+
elsif is_pubkey?; :pubkey
|
|
458
|
+
elsif is_multisig?; :multisig
|
|
459
|
+
elsif is_p2sh?; :p2sh
|
|
460
|
+
else; :unknown
|
|
270
461
|
end
|
|
271
462
|
end
|
|
272
463
|
|
|
@@ -281,7 +472,7 @@ class Bitcoin::Script
|
|
|
281
472
|
Bitcoin.pubkey_to_address(get_pubkey)
|
|
282
473
|
end
|
|
283
474
|
|
|
284
|
-
# get the hash160 for this hash160 script
|
|
475
|
+
# get the hash160 for this hash160 or pubkey script
|
|
285
476
|
def get_hash160
|
|
286
477
|
return @chunks[2..-3][0].unpack("H*")[0] if is_hash160?
|
|
287
478
|
return Bitcoin.hash160(get_pubkey) if is_pubkey?
|
|
@@ -294,19 +485,25 @@ class Bitcoin::Script
|
|
|
294
485
|
|
|
295
486
|
# get the public keys for this multisig script
|
|
296
487
|
def get_multisig_pubkeys
|
|
297
|
-
1.upto(@chunks[-2] - 80).map
|
|
488
|
+
1.upto(@chunks[-2] - 80).map{|i| @chunks[i] }
|
|
298
489
|
end
|
|
299
490
|
|
|
300
491
|
# get the pubkey addresses for this multisig script
|
|
301
492
|
def get_multisig_addresses
|
|
302
|
-
get_multisig_pubkeys.map
|
|
493
|
+
get_multisig_pubkeys.map{|pub|
|
|
494
|
+
begin
|
|
495
|
+
Bitcoin::Key.new(nil, pub.unpack("H*")[0]).addr
|
|
496
|
+
rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
|
|
497
|
+
end
|
|
498
|
+
}.compact
|
|
303
499
|
end
|
|
304
500
|
|
|
305
501
|
# get all addresses this script corresponds to (if possible)
|
|
306
502
|
def get_addresses
|
|
307
|
-
return [get_pubkey_address]
|
|
308
|
-
return [get_hash160_address]
|
|
503
|
+
return [get_pubkey_address] if is_pubkey?
|
|
504
|
+
return [get_hash160_address] if is_hash160?
|
|
309
505
|
return get_multisig_addresses if is_multisig?
|
|
506
|
+
[]
|
|
310
507
|
end
|
|
311
508
|
|
|
312
509
|
# get single address, or first for multisig script
|
|
@@ -352,7 +549,19 @@ class Bitcoin::Script
|
|
|
352
549
|
def self.to_pubkey_script_sig(signature, pubkey)
|
|
353
550
|
hash_type = "\x01"
|
|
354
551
|
#pubkey = [pubkey].pack("H*") if pubkey.bytesize != 65
|
|
355
|
-
|
|
552
|
+
return [ [signature.bytesize+1].pack("C"), signature, hash_type ].join unless pubkey
|
|
553
|
+
|
|
554
|
+
case pubkey[0]
|
|
555
|
+
when "\x04"
|
|
556
|
+
expected_size = 65
|
|
557
|
+
when "\x02", "\x03"
|
|
558
|
+
expected_size = 33
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
if !expected_size || pubkey.bytesize != expected_size
|
|
562
|
+
raise "pubkey is not in binary form"
|
|
563
|
+
end
|
|
564
|
+
|
|
356
565
|
[ [signature.bytesize+1].pack("C"), signature, hash_type, [pubkey.bytesize].pack("C"), pubkey ].join
|
|
357
566
|
end
|
|
358
567
|
|
|
@@ -373,8 +582,17 @@ class Bitcoin::Script
|
|
|
373
582
|
## OPCODES
|
|
374
583
|
|
|
375
584
|
# Does nothing
|
|
376
|
-
def op_nop
|
|
377
|
-
end
|
|
585
|
+
def op_nop; end
|
|
586
|
+
def op_nop1; end
|
|
587
|
+
def op_nop2; end
|
|
588
|
+
def op_nop3; end
|
|
589
|
+
def op_nop4; end
|
|
590
|
+
def op_nop5; end
|
|
591
|
+
def op_nop6; end
|
|
592
|
+
def op_nop7; end
|
|
593
|
+
def op_nop8; end
|
|
594
|
+
def op_nop9; end
|
|
595
|
+
def op_nop10; end
|
|
378
596
|
|
|
379
597
|
# Duplicates the top stack item.
|
|
380
598
|
def op_dup
|
|
@@ -383,31 +601,31 @@ class Bitcoin::Script
|
|
|
383
601
|
|
|
384
602
|
# The input is hashed using SHA-256.
|
|
385
603
|
def op_sha256
|
|
386
|
-
buf =
|
|
604
|
+
buf = pop_string
|
|
387
605
|
@stack << Digest::SHA256.digest(buf)
|
|
388
606
|
end
|
|
389
607
|
|
|
390
608
|
# The input is hashed using SHA-1.
|
|
391
609
|
def op_sha1
|
|
392
|
-
buf =
|
|
610
|
+
buf = pop_string
|
|
393
611
|
@stack << Digest::SHA1.digest(buf)
|
|
394
612
|
end
|
|
395
613
|
|
|
396
614
|
# The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
|
|
397
615
|
def op_hash160
|
|
398
|
-
buf =
|
|
616
|
+
buf = pop_string
|
|
399
617
|
@stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
|
|
400
618
|
end
|
|
401
619
|
|
|
402
620
|
# The input is hashed using RIPEMD-160.
|
|
403
621
|
def op_ripemd160
|
|
404
|
-
buf =
|
|
622
|
+
buf = pop_string
|
|
405
623
|
@stack << Digest::RMD160.digest(buf)
|
|
406
624
|
end
|
|
407
625
|
|
|
408
626
|
# The input is hashed two times with SHA-256.
|
|
409
627
|
def op_hash256
|
|
410
|
-
buf =
|
|
628
|
+
buf = pop_string
|
|
411
629
|
@stack << Digest::SHA256.digest(Digest::SHA256.digest(buf))
|
|
412
630
|
end
|
|
413
631
|
|
|
@@ -428,33 +646,103 @@ class Bitcoin::Script
|
|
|
428
646
|
|
|
429
647
|
# The top two items on the stack are swapped.
|
|
430
648
|
def op_swap
|
|
431
|
-
@stack[-2..-1] = @stack[-2..-1].reverse
|
|
649
|
+
@stack[-2..-1] = @stack[-2..-1].reverse if @stack[-2]
|
|
432
650
|
end
|
|
433
651
|
|
|
434
652
|
# If both a and b are not 0, the output is 1. Otherwise 0.
|
|
435
653
|
def op_booland
|
|
436
|
-
a, b =
|
|
654
|
+
a, b = pop_int(2)
|
|
437
655
|
@stack << (![a,b].any?{|n| n == 0 } ? 1 : 0)
|
|
438
656
|
end
|
|
439
657
|
|
|
658
|
+
# If a or b is not 0, the output is 1. Otherwise 0.
|
|
659
|
+
def op_boolor
|
|
660
|
+
a, b = pop_int(2)
|
|
661
|
+
@stack << ( (a != 0 || b != 0) ? 1 : 0 )
|
|
662
|
+
end
|
|
663
|
+
|
|
440
664
|
# a is added to b.
|
|
441
665
|
def op_add
|
|
442
|
-
a, b =
|
|
666
|
+
a, b = pop_int(2)
|
|
443
667
|
@stack << a + b
|
|
444
668
|
end
|
|
445
669
|
|
|
446
670
|
# b is subtracted from a.
|
|
447
671
|
def op_sub
|
|
448
|
-
a, b =
|
|
672
|
+
a, b = pop_int(2)
|
|
449
673
|
@stack << a - b
|
|
450
674
|
end
|
|
451
675
|
|
|
676
|
+
# Returns 1 if a is less than b, 0 otherwise.
|
|
677
|
+
def op_lessthan
|
|
678
|
+
a, b = pop_int(2)
|
|
679
|
+
@stack << (a < b ? 1 : 0)
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
# Returns 1 if a is less than or equal to b, 0 otherwise.
|
|
683
|
+
def op_lessthanorequal
|
|
684
|
+
a, b = pop_int(2)
|
|
685
|
+
@stack << (a <= b ? 1 : 0)
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
# Returns 1 if a is greater than b, 0 otherwise.
|
|
689
|
+
def op_greaterthan
|
|
690
|
+
a, b = pop_int(2)
|
|
691
|
+
@stack << (a > b ? 1 : 0)
|
|
692
|
+
end
|
|
693
|
+
|
|
452
694
|
# Returns 1 if a is greater than or equal to b, 0 otherwise.
|
|
453
695
|
def op_greaterthanorequal
|
|
454
|
-
a, b =
|
|
696
|
+
a, b = pop_int(2)
|
|
455
697
|
@stack << (a >= b ? 1 : 0)
|
|
456
698
|
end
|
|
457
699
|
|
|
700
|
+
# If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
|
|
701
|
+
def op_not
|
|
702
|
+
a = pop_int
|
|
703
|
+
@stack << (a == 0 ? 1 : 0)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
def op_0notequal
|
|
707
|
+
a = pop_int
|
|
708
|
+
@stack << (a != 0 ? 1 : 0)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# The input is made positive.
|
|
712
|
+
def op_abs
|
|
713
|
+
a = pop_int
|
|
714
|
+
@stack << a.abs
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
# The input is divided by 2. Currently disabled.
|
|
718
|
+
def op_2div
|
|
719
|
+
a = pop_int
|
|
720
|
+
@stack << (a >> 1)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
# The input is multiplied by 2. Currently disabled.
|
|
724
|
+
def op_2mul
|
|
725
|
+
a = pop_int
|
|
726
|
+
@stack << (a << 1)
|
|
727
|
+
end
|
|
728
|
+
|
|
729
|
+
# 1 is added to the input.
|
|
730
|
+
def op_1add
|
|
731
|
+
a = pop_int
|
|
732
|
+
@stack << (a + 1)
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
def op_1sub
|
|
736
|
+
a = pop_int
|
|
737
|
+
@stack << (a - 1)
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
# The sign of the input is flipped.
|
|
741
|
+
def op_negate
|
|
742
|
+
a = pop_int
|
|
743
|
+
@stack << -a
|
|
744
|
+
end
|
|
745
|
+
|
|
458
746
|
# Removes the top stack item.
|
|
459
747
|
def op_drop
|
|
460
748
|
@stack.pop
|
|
@@ -462,13 +750,14 @@ class Bitcoin::Script
|
|
|
462
750
|
|
|
463
751
|
# Returns 1 if the inputs are exactly equal, 0 otherwise.
|
|
464
752
|
def op_equal
|
|
465
|
-
a, b = @stack.pop(2)
|
|
753
|
+
#a, b = @stack.pop(2)
|
|
754
|
+
a, b = pop_int(2)
|
|
466
755
|
@stack << (a == b ? 1 : 0)
|
|
467
756
|
end
|
|
468
757
|
|
|
469
758
|
# Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
|
|
470
759
|
def op_verify
|
|
471
|
-
res =
|
|
760
|
+
res = pop_int
|
|
472
761
|
if res == 0
|
|
473
762
|
@stack << res
|
|
474
763
|
@script_invalid = true # raise 'transaction invalid' ?
|
|
@@ -494,30 +783,30 @@ class Bitcoin::Script
|
|
|
494
783
|
|
|
495
784
|
# Returns the smaller of a and b.
|
|
496
785
|
def op_min
|
|
497
|
-
@stack <<
|
|
786
|
+
@stack << pop_int(2).min
|
|
498
787
|
end
|
|
499
788
|
|
|
500
789
|
# Returns the larger of a and b.
|
|
501
790
|
def op_max
|
|
502
|
-
@stack <<
|
|
791
|
+
@stack << pop_int(2).max
|
|
503
792
|
end
|
|
504
|
-
|
|
793
|
+
|
|
505
794
|
# Copies the pair of items two spaces back in the stack to the front.
|
|
506
795
|
def op_2over
|
|
507
796
|
@stack << @stack[-4]
|
|
508
797
|
@stack << @stack[-4]
|
|
509
798
|
end
|
|
510
|
-
|
|
799
|
+
|
|
511
800
|
# Swaps the top two pairs of items.
|
|
512
801
|
def op_2swap
|
|
513
802
|
p1 = @stack.pop(2)
|
|
514
803
|
p2 = @stack.pop(2)
|
|
515
804
|
@stack += p1 += p2
|
|
516
805
|
end
|
|
517
|
-
|
|
806
|
+
|
|
518
807
|
# If the input is true, duplicate it.
|
|
519
808
|
def op_ifdup
|
|
520
|
-
if @stack.last != 0
|
|
809
|
+
if cast_to_bignum(@stack.last) != 0
|
|
521
810
|
@stack << @stack.last
|
|
522
811
|
end
|
|
523
812
|
end
|
|
@@ -526,38 +815,189 @@ class Bitcoin::Script
|
|
|
526
815
|
def op_1negate
|
|
527
816
|
@stack << -1
|
|
528
817
|
end
|
|
529
|
-
|
|
818
|
+
|
|
530
819
|
# Puts the number of stack items onto the stack.
|
|
531
820
|
def op_depth
|
|
532
821
|
@stack << @stack.size
|
|
533
822
|
end
|
|
534
823
|
|
|
535
|
-
#
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
824
|
+
# Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
|
|
825
|
+
def op_within
|
|
826
|
+
bn1, bn2, bn3 = pop_int(3)
|
|
827
|
+
@stack << ( (bn2 <= bn1 && bn1 < bn3) ? 1 : 0 )
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
# Returns 1 if the numbers are equal, 0 otherwise.
|
|
831
|
+
def op_numequal
|
|
832
|
+
a, b = pop_int(2)
|
|
833
|
+
@stack << (a == b ? 1 : 0)
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
# Returns 1 if the numbers are not equal, 0 otherwise.
|
|
837
|
+
def op_numnotequal
|
|
838
|
+
a, b = pop_int(2)
|
|
839
|
+
@stack << (a != b ? 1 : 0)
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
# Marks transaction as invalid.
|
|
843
|
+
def op_return
|
|
844
|
+
@script_invalid = true; nil
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
# Copies the second-to-top stack item to the top.
|
|
848
|
+
def op_over
|
|
849
|
+
item = @stack[-2]
|
|
850
|
+
@stack << item if item
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
# If the top stack value is not 0, the statements are executed. The top stack value is removed.
|
|
854
|
+
def op_if
|
|
855
|
+
value = false
|
|
856
|
+
if @do_exec
|
|
857
|
+
return if @stack.size < 1
|
|
858
|
+
value = pop_int == 1 ? true : false
|
|
859
|
+
end
|
|
860
|
+
@exec_stack << value
|
|
861
|
+
end
|
|
862
|
+
|
|
863
|
+
# If the top stack value is 0, the statements are executed. The top stack value is removed.
|
|
864
|
+
def op_notif
|
|
865
|
+
value = false
|
|
866
|
+
if @do_exec
|
|
867
|
+
return if @stack.size < 1
|
|
868
|
+
value = pop_int == 1 ? false : true
|
|
869
|
+
end
|
|
870
|
+
@exec_stack << value
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
# If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed then these statements are and if the preceding OP_IF or OP_NOTIF or OP_ELSE was executed then these statements are not.
|
|
874
|
+
def op_else
|
|
875
|
+
return if @exec_stack.empty?
|
|
876
|
+
@exec_stack[-1] = !@exec_stack[-1]
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
# Ends an if/else block.
|
|
880
|
+
def op_endif
|
|
881
|
+
return if @exec_stack.empty?
|
|
882
|
+
@exec_stack.pop
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
# The item n back in the stack is copied to the top.
|
|
886
|
+
def op_pick
|
|
887
|
+
pos = pop_int
|
|
888
|
+
item = @stack[-(pos+1)]
|
|
889
|
+
@stack << item if item
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
# The item n back in the stack is moved to the top.
|
|
893
|
+
def op_roll
|
|
894
|
+
pos = pop_int
|
|
895
|
+
idx = -(pos+1)
|
|
896
|
+
item = @stack[idx]
|
|
897
|
+
if item
|
|
898
|
+
@stack.delete_at(idx)
|
|
899
|
+
@stack << item if item
|
|
540
900
|
end
|
|
541
901
|
end
|
|
542
902
|
|
|
903
|
+
# The top three items on the stack are rotated to the left.
|
|
904
|
+
def op_rot
|
|
905
|
+
return if @stack.size < 3
|
|
906
|
+
@stack[-3..-1] = [ @stack[-2], @stack[-1], @stack[-3] ]
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
# Removes the top two stack items.
|
|
910
|
+
def op_2drop
|
|
911
|
+
@stack.pop(2)
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
# Duplicates the top two stack items.
|
|
915
|
+
def op_2dup
|
|
916
|
+
@stack.push(*@stack[-2..-1])
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
# Duplicates the top three stack items.
|
|
920
|
+
def op_3dup
|
|
921
|
+
@stack.push(*@stack[-3..-1])
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
# Removes the second-to-top stack item.
|
|
925
|
+
def op_nip
|
|
926
|
+
@stack.delete_at(-2)
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
# Returns the length of the input string.
|
|
930
|
+
def op_size
|
|
931
|
+
item = @stack[-1]
|
|
932
|
+
size = case item
|
|
933
|
+
when String; item.bytesize
|
|
934
|
+
when Numeric; OpenSSL::BN.new(item.to_s).to_mpi.size - 4
|
|
935
|
+
end
|
|
936
|
+
@stack << size
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
# Transaction is invalid unless occuring in an unexecuted OP_IF branch
|
|
940
|
+
def op_ver
|
|
941
|
+
invalid if @do_exec
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
def pop_int(count=nil)
|
|
945
|
+
return cast_to_bignum(@stack.pop) unless count
|
|
946
|
+
@stack.pop(count).map{|i| cast_to_bignum(i) }
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
def pop_string(count=nil)
|
|
950
|
+
return cast_to_string(@stack.pop) unless count
|
|
951
|
+
@stack.pop(count).map{|i| cast_to_string(i) }
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
def cast_to_bignum(buf)
|
|
955
|
+
case buf
|
|
956
|
+
when Numeric; buf
|
|
957
|
+
when String; OpenSSL::BN.new([buf.bytesize].pack("N") + buf.reverse, 0).to_i
|
|
958
|
+
else; raise TypeError, 'cast_to_bignum: failed to cast: %s (%s)' % [buf, buf.class]
|
|
959
|
+
end
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
def cast_to_string(buf)
|
|
963
|
+
case buf
|
|
964
|
+
when Numeric; OpenSSL::BN.new(buf.to_s).to_s(0)[4..-1]
|
|
965
|
+
when String; buf;
|
|
966
|
+
else; raise TypeError, 'cast_to_string: failed to cast: %s (%s)' % [buf, buf.class]
|
|
967
|
+
end
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
# Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
|
|
971
|
+
def op_numequalverify
|
|
972
|
+
op_numequal; op_verify
|
|
973
|
+
end
|
|
974
|
+
|
|
543
975
|
# All of the signature checking words will only match signatures
|
|
544
976
|
# to the data after the most recently-executed OP_CODESEPARATOR.
|
|
545
977
|
def op_codeseparator
|
|
546
978
|
@codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
|
|
547
979
|
end
|
|
548
980
|
|
|
981
|
+
def codehash_script(opcode)
|
|
982
|
+
# CScript scriptCode(pbegincodehash, pend);
|
|
983
|
+
script = to_string(@chunks[(@codehash_start||0)...@chunks.size-@chunks.reverse.index(opcode)])
|
|
984
|
+
checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
|
|
985
|
+
[script, checkhash]
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
|
|
549
989
|
# do a CHECKSIG operation on the current stack,
|
|
550
990
|
# asking +check_callback+ to do the actual signature verification.
|
|
551
991
|
# This is used by Protocol::Tx#verify_input_signature
|
|
552
992
|
def op_checksig(check_callback)
|
|
553
993
|
return invalid if @stack.size < 2
|
|
554
994
|
pubkey = @stack.pop
|
|
555
|
-
drop_sigs = [@stack[-1]
|
|
995
|
+
drop_sigs = [ @stack[-1] ]
|
|
556
996
|
sig, hash_type = parse_sig(@stack.pop)
|
|
557
997
|
|
|
558
|
-
if
|
|
559
|
-
|
|
560
|
-
|
|
998
|
+
if inner_p2sh?
|
|
999
|
+
script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
|
|
1000
|
+
drop_sigs = nil
|
|
561
1001
|
else
|
|
562
1002
|
script_code, drop_sigs = nil, nil
|
|
563
1003
|
end
|
|
@@ -589,23 +1029,22 @@ class Bitcoin::Script
|
|
|
589
1029
|
# TODO: validate signature order
|
|
590
1030
|
# TODO: take global opcode count
|
|
591
1031
|
def op_checkmultisig(check_callback)
|
|
592
|
-
n_pubkeys =
|
|
1032
|
+
n_pubkeys = pop_int
|
|
593
1033
|
return invalid unless (0..20).include?(n_pubkeys)
|
|
594
1034
|
return invalid unless @stack.last(n_pubkeys).all?{|e| e.is_a?(String) && e != '' }
|
|
595
1035
|
#return invalid if ((@op_count ||= 0) += n_pubkeys) > 201
|
|
596
|
-
pubkeys =
|
|
1036
|
+
pubkeys = pop_string(n_pubkeys)
|
|
597
1037
|
|
|
598
|
-
n_sigs =
|
|
1038
|
+
n_sigs = pop_int
|
|
599
1039
|
return invalid unless (0..n_pubkeys).include?(n_sigs)
|
|
600
1040
|
return invalid unless @stack.last(n_sigs).all?{|e| e.is_a?(String) && e != '' }
|
|
601
|
-
sigs = (drop_sigs =
|
|
1041
|
+
sigs = (drop_sigs = pop_string(n_sigs)).map{|s| parse_sig(s) }
|
|
602
1042
|
|
|
603
1043
|
@stack.pop if @stack[-1] == '' # remove OP_NOP from stack
|
|
604
1044
|
|
|
605
|
-
if
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
drop_sigs.map!{|i| i.unpack("H*")[0] }
|
|
1045
|
+
if inner_p2sh?
|
|
1046
|
+
script_code = @inner_script_code || to_binary_without_signatures(drop_sigs)
|
|
1047
|
+
drop_sigs = nil
|
|
609
1048
|
else
|
|
610
1049
|
script_code, drop_sigs = nil, nil
|
|
611
1050
|
end
|
|
@@ -615,7 +1054,12 @@ class Bitcoin::Script
|
|
|
615
1054
|
valid_sigs += 1 if check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code)
|
|
616
1055
|
}}
|
|
617
1056
|
|
|
618
|
-
@stack << ((valid_sigs
|
|
1057
|
+
@stack << ((valid_sigs >= n_sigs) ? 1 : (invalid; 0))
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
# op_eval: https://en.bitcoin.it/wiki/BIP_0012
|
|
1061
|
+
# the BIP was never accepted and must be handled as old OP_NOP1
|
|
1062
|
+
def op_nop1
|
|
619
1063
|
end
|
|
620
1064
|
|
|
621
1065
|
OPCODES_METHOD = Hash[*instance_methods.grep(/^op_/).map{|m|
|
|
@@ -624,6 +1068,19 @@ class Bitcoin::Script
|
|
|
624
1068
|
OPCODES_METHOD[0] = :op_0
|
|
625
1069
|
OPCODES_METHOD[81] = :op_1
|
|
626
1070
|
|
|
1071
|
+
def self.is_canonical_pubkey?(pubkey)
|
|
1072
|
+
return false if pubkey.bytesize < 33 # "Non-canonical public key: too short"
|
|
1073
|
+
case pubkey[0]
|
|
1074
|
+
when "\x04"
|
|
1075
|
+
return false if pubkey.bytesize != 65 # "Non-canonical public key: invalid length for uncompressed key"
|
|
1076
|
+
when "\x02", "\x03"
|
|
1077
|
+
return false if pubkey.bytesize != 33 # "Non-canonical public key: invalid length for compressed key"
|
|
1078
|
+
else
|
|
1079
|
+
return false # "Non-canonical public key: compressed nor uncompressed"
|
|
1080
|
+
end
|
|
1081
|
+
true
|
|
1082
|
+
end
|
|
1083
|
+
|
|
627
1084
|
private
|
|
628
1085
|
|
|
629
1086
|
def parse_sig(sig)
|