tapyrus 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +12 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +6 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +100 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/exe/tapyrusrb-cli +5 -0
  15. data/exe/tapyrusrbd +41 -0
  16. data/lib/openassets/marker_output.rb +20 -0
  17. data/lib/openassets/payload.rb +54 -0
  18. data/lib/openassets/util.rb +28 -0
  19. data/lib/openassets.rb +9 -0
  20. data/lib/tapyrus/base58.rb +38 -0
  21. data/lib/tapyrus/block.rb +77 -0
  22. data/lib/tapyrus/block_header.rb +88 -0
  23. data/lib/tapyrus/bloom_filter.rb +78 -0
  24. data/lib/tapyrus/chain_params.rb +90 -0
  25. data/lib/tapyrus/chainparams/mainnet.yml +41 -0
  26. data/lib/tapyrus/chainparams/regtest.yml +38 -0
  27. data/lib/tapyrus/chainparams/testnet.yml +41 -0
  28. data/lib/tapyrus/constants.rb +195 -0
  29. data/lib/tapyrus/descriptor.rb +147 -0
  30. data/lib/tapyrus/ext_key.rb +337 -0
  31. data/lib/tapyrus/key.rb +296 -0
  32. data/lib/tapyrus/key_path.rb +26 -0
  33. data/lib/tapyrus/logger.rb +42 -0
  34. data/lib/tapyrus/merkle_tree.rb +149 -0
  35. data/lib/tapyrus/message/addr.rb +35 -0
  36. data/lib/tapyrus/message/base.rb +28 -0
  37. data/lib/tapyrus/message/block.rb +46 -0
  38. data/lib/tapyrus/message/block_transaction_request.rb +45 -0
  39. data/lib/tapyrus/message/block_transactions.rb +31 -0
  40. data/lib/tapyrus/message/block_txn.rb +27 -0
  41. data/lib/tapyrus/message/cmpct_block.rb +42 -0
  42. data/lib/tapyrus/message/error.rb +10 -0
  43. data/lib/tapyrus/message/fee_filter.rb +27 -0
  44. data/lib/tapyrus/message/filter_add.rb +28 -0
  45. data/lib/tapyrus/message/filter_clear.rb +17 -0
  46. data/lib/tapyrus/message/filter_load.rb +39 -0
  47. data/lib/tapyrus/message/get_addr.rb +17 -0
  48. data/lib/tapyrus/message/get_block_txn.rb +27 -0
  49. data/lib/tapyrus/message/get_blocks.rb +29 -0
  50. data/lib/tapyrus/message/get_data.rb +21 -0
  51. data/lib/tapyrus/message/get_headers.rb +28 -0
  52. data/lib/tapyrus/message/header_and_short_ids.rb +57 -0
  53. data/lib/tapyrus/message/headers.rb +35 -0
  54. data/lib/tapyrus/message/headers_parser.rb +24 -0
  55. data/lib/tapyrus/message/inv.rb +21 -0
  56. data/lib/tapyrus/message/inventories_parser.rb +23 -0
  57. data/lib/tapyrus/message/inventory.rb +51 -0
  58. data/lib/tapyrus/message/mem_pool.rb +17 -0
  59. data/lib/tapyrus/message/merkle_block.rb +42 -0
  60. data/lib/tapyrus/message/network_addr.rb +63 -0
  61. data/lib/tapyrus/message/not_found.rb +21 -0
  62. data/lib/tapyrus/message/ping.rb +30 -0
  63. data/lib/tapyrus/message/pong.rb +26 -0
  64. data/lib/tapyrus/message/prefilled_tx.rb +29 -0
  65. data/lib/tapyrus/message/reject.rb +46 -0
  66. data/lib/tapyrus/message/send_cmpct.rb +43 -0
  67. data/lib/tapyrus/message/send_headers.rb +16 -0
  68. data/lib/tapyrus/message/tx.rb +30 -0
  69. data/lib/tapyrus/message/ver_ack.rb +17 -0
  70. data/lib/tapyrus/message/version.rb +69 -0
  71. data/lib/tapyrus/message.rb +70 -0
  72. data/lib/tapyrus/mnemonic/wordlist/chinese_simplified.txt +2048 -0
  73. data/lib/tapyrus/mnemonic/wordlist/chinese_traditional.txt +2048 -0
  74. data/lib/tapyrus/mnemonic/wordlist/english.txt +2048 -0
  75. data/lib/tapyrus/mnemonic/wordlist/french.txt +2048 -0
  76. data/lib/tapyrus/mnemonic/wordlist/italian.txt +2048 -0
  77. data/lib/tapyrus/mnemonic/wordlist/japanese.txt +2048 -0
  78. data/lib/tapyrus/mnemonic/wordlist/spanish.txt +2048 -0
  79. data/lib/tapyrus/mnemonic.rb +77 -0
  80. data/lib/tapyrus/network/connection.rb +73 -0
  81. data/lib/tapyrus/network/message_handler.rb +241 -0
  82. data/lib/tapyrus/network/peer.rb +223 -0
  83. data/lib/tapyrus/network/peer_discovery.rb +42 -0
  84. data/lib/tapyrus/network/pool.rb +135 -0
  85. data/lib/tapyrus/network.rb +13 -0
  86. data/lib/tapyrus/node/cli.rb +112 -0
  87. data/lib/tapyrus/node/configuration.rb +38 -0
  88. data/lib/tapyrus/node/spv.rb +79 -0
  89. data/lib/tapyrus/node.rb +7 -0
  90. data/lib/tapyrus/opcodes.rb +178 -0
  91. data/lib/tapyrus/out_point.rb +44 -0
  92. data/lib/tapyrus/rpc/http_server.rb +65 -0
  93. data/lib/tapyrus/rpc/request_handler.rb +150 -0
  94. data/lib/tapyrus/rpc/tapyrus_core_client.rb +72 -0
  95. data/lib/tapyrus/rpc.rb +7 -0
  96. data/lib/tapyrus/script/multisig.rb +92 -0
  97. data/lib/tapyrus/script/script.rb +551 -0
  98. data/lib/tapyrus/script/script_error.rb +111 -0
  99. data/lib/tapyrus/script/script_interpreter.rb +668 -0
  100. data/lib/tapyrus/script/tx_checker.rb +81 -0
  101. data/lib/tapyrus/script_witness.rb +38 -0
  102. data/lib/tapyrus/secp256k1/native.rb +174 -0
  103. data/lib/tapyrus/secp256k1/ruby.rb +123 -0
  104. data/lib/tapyrus/secp256k1.rb +12 -0
  105. data/lib/tapyrus/slip39/share.rb +122 -0
  106. data/lib/tapyrus/slip39/sss.rb +245 -0
  107. data/lib/tapyrus/slip39/wordlist/english.txt +1024 -0
  108. data/lib/tapyrus/slip39.rb +93 -0
  109. data/lib/tapyrus/store/chain_entry.rb +67 -0
  110. data/lib/tapyrus/store/db/level_db.rb +98 -0
  111. data/lib/tapyrus/store/db.rb +9 -0
  112. data/lib/tapyrus/store/spv_chain.rb +101 -0
  113. data/lib/tapyrus/store.rb +9 -0
  114. data/lib/tapyrus/tx.rb +347 -0
  115. data/lib/tapyrus/tx_in.rb +89 -0
  116. data/lib/tapyrus/tx_out.rb +74 -0
  117. data/lib/tapyrus/util.rb +133 -0
  118. data/lib/tapyrus/validation.rb +115 -0
  119. data/lib/tapyrus/version.rb +3 -0
  120. data/lib/tapyrus/wallet/account.rb +151 -0
  121. data/lib/tapyrus/wallet/base.rb +162 -0
  122. data/lib/tapyrus/wallet/db.rb +81 -0
  123. data/lib/tapyrus/wallet/master_key.rb +110 -0
  124. data/lib/tapyrus/wallet.rb +8 -0
  125. data/lib/tapyrus.rb +219 -0
  126. data/tapyrusrb.conf.sample +0 -0
  127. data/tapyrusrb.gemspec +47 -0
  128. metadata +451 -0
@@ -0,0 +1,135 @@
1
+ module Tapyrus
2
+
3
+ module Network
4
+
5
+ # Time between pings automatically sent out for latency probing and keepalive (in seconds).
6
+ PING_INTERVAL = 2 * 60
7
+ # Time after which to disconnect, after waiting for a ping response (or inactivity).
8
+ TIMEOUT_INTERVAL = 20 * 60
9
+ # Maximum number of automatic outgoing nodes
10
+ MAX_OUTBOUND_CONNECTIONS = 4
11
+
12
+ # peer pool class.
13
+ class Pool
14
+ include Observable
15
+
16
+ attr_reader :peers # active peers
17
+ attr_reader :pending_peers # currently connecting peer
18
+ attr_reader :node
19
+ attr_reader :chain
20
+ attr_reader :max_outbound
21
+ attr_reader :logger
22
+ attr_reader :peer_discovery
23
+ attr_accessor :started
24
+ attr_reader :mutex
25
+
26
+ def initialize(node, chain, configuration)
27
+ @node = node
28
+ @peers = []
29
+ @pending_peers = []
30
+ @max_outbound = MAX_OUTBOUND_CONNECTIONS
31
+ @chain = chain
32
+ @logger = Tapyrus::Logger.create(:debug)
33
+ @configuration = configuration
34
+ @peer_discovery = PeerDiscovery.new(configuration)
35
+ @started = false
36
+ @mutex = Mutex.new
37
+ end
38
+
39
+ # connecting other peers and begin network activity.
40
+ def start
41
+ raise 'Cannot start a peer pool twice.' if started
42
+ logger.debug 'Start connecting other pears.'
43
+ addr_list = peer_discovery.peers
44
+
45
+ connect(addr_list)
46
+
47
+ @started = true
48
+ end
49
+
50
+ # detect new peer connection.
51
+ def handle_new_peer(peer)
52
+ logger.debug "connected new peer #{peer.addr}."
53
+ mutex.synchronize do
54
+ peer.id = allocate_peer_id
55
+ unless peers.find(&:primary?)
56
+ peer.primary = true
57
+ peer.start_block_header_download
58
+ end
59
+ peers << peer
60
+ end
61
+ pending_peers.delete(peer)
62
+ filter_load(peer) if node.wallet
63
+ end
64
+
65
+ def handle_close_peer(peer)
66
+ return unless started
67
+ peers.delete(peer)
68
+ pending_peers.delete(peer)
69
+ addr_list = peer_discovery.peers - peers.map(&:host) - pending_peers.map(&:host) - [peer.host]
70
+ connect(addr_list)
71
+ end
72
+
73
+ # terminate peers.
74
+ def terminate
75
+ peers.each { |peer| peer.close('terminate') }
76
+ pending_peers.each { |peer| peer.close('terminate') }
77
+ @peers = []
78
+ @started = false
79
+ end
80
+
81
+ # broadcast tx to connecting peer.
82
+ def broadcast(tx)
83
+ peers.each { |peer| peer.broadcast_tx(tx) }
84
+ end
85
+
86
+ # new bloom filter.
87
+ def filter_load(peer)
88
+ peer.send_filter_load(node.bloom)
89
+ end
90
+
91
+ # add element to bloom filter.
92
+ def filter_add(element)
93
+ peers.each { |peer| peer.send_filter_add(element) }
94
+ end
95
+
96
+ # clear bloom filter.
97
+ def filter_clear
98
+ peers.each { |peer| peer.send_filter_clear }
99
+ end
100
+
101
+ def handle_error(e)
102
+ terminate
103
+ end
104
+
105
+ private
106
+
107
+ # get primary peer
108
+ def primary_peer
109
+ peers.find(&:primary?)
110
+ end
111
+
112
+ # allocate new peer id
113
+ def allocate_peer_id
114
+ id = 0
115
+ until peers.empty? || peers.find{|p|p.id == id}.nil?
116
+ id += 1
117
+ end
118
+ id
119
+ end
120
+
121
+ def connect(addr_list)
122
+ port = Tapyrus.chain_params.default_port
123
+
124
+ EM::Iterator.new(addr_list, Tapyrus::PARALLEL_THREAD).each do |ip, iter|
125
+ if pending_peers.size + peers.size < MAX_OUTBOUND_CONNECTIONS
126
+ peer = Peer.new(ip, port, self, @configuration)
127
+ pending_peers << peer
128
+ peer.connect
129
+ iter.next
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,13 @@
1
+ require 'eventmachine'
2
+
3
+ module Tapyrus
4
+ module Network
5
+
6
+ autoload :MessageHandler, 'tapyrus/network/message_handler'
7
+ autoload :Connection, 'tapyrus/network/connection'
8
+ autoload :Pool, 'tapyrus/network/pool'
9
+ autoload :Peer, 'tapyrus/network/peer'
10
+ autoload :PeerDiscovery, 'tapyrus/network/peer_discovery'
11
+
12
+ end
13
+ end
@@ -0,0 +1,112 @@
1
+ require 'rest-client'
2
+ require 'thor'
3
+ require 'json'
4
+
5
+ module Tapyrus
6
+ module Node
7
+
8
+ class CLI < Thor
9
+
10
+ class_option :network, aliases: '-n', default: :mainnet
11
+
12
+ desc 'getblockchaininfo', 'Returns an object containing various state info regarding blockchain processing.'
13
+ def getblockchaininfo
14
+ request('getblockchaininfo')
15
+ end
16
+
17
+ desc 'stop', 'Stop Tapyrus server.'
18
+ def stop
19
+ request('stop')
20
+ end
21
+
22
+ desc 'getblockheader "hash" ( verbose )', 'If verbose is false, returns a string that is serialized, hex-encoded data for blockheader "hash". If verbose is true, returns an Object with information about blockheader <hash>.'
23
+ def getblockheader(hash, verbose = true)
24
+ verbose = verbose.is_a?(String) ? (verbose == 'true') : verbose
25
+ request('getblockheader', hash, verbose)
26
+ end
27
+
28
+ desc 'getpeerinfo', 'Returns data about each connected network node as a json array of objects.'
29
+ def getpeerinfo
30
+ request('getpeerinfo')
31
+ end
32
+
33
+ desc 'decoderawtransaction "hexstring"', 'Return a JSON object representing the serialized, hex-encoded transaction.'
34
+ def decoderawtransaction(hexstring)
35
+ request('decoderawtransaction', hexstring)
36
+ end
37
+
38
+ desc 'decodescript "hexstring"', 'Decode a hex-encoded script.'
39
+ def decodescript(hexstring)
40
+ request('decodescript', hexstring)
41
+ end
42
+
43
+ # wallet cli
44
+
45
+ desc 'sendrawtransaction', 'Submits raw transaction (serialized, hex-encoded) to local node and network.'
46
+ def sendrawtransaction(hex_tx)
47
+ request('sendrawtransaction', hex_tx)
48
+ end
49
+
50
+ desc 'createwallet "wallet_id"', 'Create new HD wallet. It returns an error if an existing wallet_id is specified. '
51
+ def createwallet(wallet_id)
52
+ request('createwallet', wallet_id)
53
+ end
54
+
55
+ desc 'listwallets', 'Returns a list of currently loaded wallets. For full information on the wallet, use "getwalletinfo"'
56
+ def listwallets
57
+ request('listwallets')
58
+ end
59
+
60
+ desc 'getwalletinfo', 'Returns an object containing various wallet state info.'
61
+ def getwalletinfo
62
+ request('getwalletinfo')
63
+ end
64
+
65
+ desc 'listaccounts', '[WIP]Returns Object that has account names as keys, account balances as values.'
66
+ def listaccounts
67
+ request('listaccounts')
68
+ end
69
+
70
+ desc 'encryptwallet "passphrase"', 'Encrypts the wallet with "passphrase". This is for first time encryption.After this, any calls that interact with private keys such as sending or signing will require the passphrase to be set prior the making these calls.'
71
+ def encryptwallet(passhphrase)
72
+ request('encryptwallet', passhphrase)
73
+ end
74
+
75
+ desc 'getnewaddress "account"', 'Returns a new Tapyrus address for receiving payments.'
76
+ def getnewaddress(account)
77
+ request('getnewaddress', account)
78
+ end
79
+
80
+ private
81
+
82
+ def config
83
+ opts = {}
84
+ opts[:network] = options['network'] if options['network']
85
+ @conf ||= Tapyrus::Node::Configuration.new(opts)
86
+ end
87
+
88
+ def request(command, *params)
89
+ data = {
90
+ :method => command,
91
+ :params => params,
92
+ :id => 'jsonrpc'
93
+ }
94
+ begin
95
+ RestClient::Request.execute(method: :post, url: config.server_url, payload: data.to_json,
96
+ headers: {content_type: :json}) do |response, request, result|
97
+ return false if !result.kind_of?(Net::HTTPSuccess) && response.empty?
98
+ begin
99
+ json = JSON.parse(response.to_str)
100
+ puts JSON.pretty_generate(json)
101
+ rescue Exception
102
+ puts response.to_str
103
+ end
104
+ end
105
+ rescue Exception => e
106
+ puts e.message
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,38 @@
1
+ require 'iniparse'
2
+
3
+ module Tapyrus
4
+ module Node
5
+ class Configuration
6
+
7
+ attr_reader :conf
8
+
9
+ def initialize(opts = {})
10
+ # TODO apply configuration file.
11
+ opts[:network] = :mainnet unless opts[:network]
12
+ opts[:relay] = false unless opts[:relay]
13
+ Tapyrus.chain_params = opts[:network]
14
+
15
+ begin
16
+ ini_file = IniParse.parse(File.read("#{Tapyrus.base_dir}/tapyrusrb.conf"))
17
+ @conf = Hash[ ini_file.to_h['__anonymous__'].map{|k,v| [k.to_sym, v] } ]
18
+ rescue => e
19
+ @conf = {}
20
+ end
21
+ @conf.merge!(opts)
22
+ end
23
+
24
+ def host
25
+ 'localhost'
26
+ end
27
+
28
+ def port
29
+ Tapyrus.chain_params.default_port - 1
30
+ end
31
+
32
+ def server_url
33
+ "http://#{host}:#{port}"
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,79 @@
1
+ module Tapyrus
2
+ module Node
3
+
4
+ # SPV class
5
+ class SPV
6
+
7
+ attr_reader :chain
8
+ attr_reader :pool
9
+ attr_reader :logger
10
+ attr_accessor :running
11
+ attr_reader :configuration
12
+ attr_accessor :server
13
+ attr_accessor :wallet
14
+ attr_accessor :bloom
15
+
16
+ def initialize(configuration)
17
+ @chain = Tapyrus::Store::SPVChain.new
18
+ @configuration = configuration
19
+ @pool = Tapyrus::Network::Pool.new(self, @chain, @configuration)
20
+ @logger = Tapyrus::Logger.create(:debug)
21
+ @running = false
22
+ @wallet = Tapyrus::Wallet::Base.current_wallet
23
+ # TODO : optimize bloom filter parameters
24
+ setup_filter
25
+ end
26
+
27
+ # open the node.
28
+ def run
29
+ # TODO need process running check.
30
+ return if running
31
+ logger.debug 'SPV node start running.'
32
+ EM.run do
33
+ # EM.start_server('0.0.0.0', Tapyrus.chain_params.default_port, Tapyrus::Network::InboundConnector, self)
34
+ pool.start
35
+ @server = Tapyrus::RPC::HttpServer.run(self, configuration.port)
36
+ end
37
+ end
38
+
39
+ # close the node.
40
+ def shutdown
41
+ pool.terminate
42
+ logger.debug 'SPV node shutdown.'
43
+ end
44
+
45
+ # broadcast a transaction
46
+ def broadcast(tx)
47
+ pool.broadcast(tx)
48
+ logger.debug "broadcast tx: #{tx.to_payload.bth}"
49
+ end
50
+
51
+ # add filter element to bloom filter.
52
+ # [String] element. the hex string of txid, public key, public key hash or outpoint.
53
+ def filter_add(element)
54
+ bloom.add(element)
55
+ pool.filter_add(element)
56
+ end
57
+
58
+ # clear bloom filter.
59
+ def filter_clear
60
+ pool.filter_clear
61
+ end
62
+
63
+ def add_observer(observer)
64
+ pool.add_observer(observer)
65
+ end
66
+
67
+ def delete_observer(observer)
68
+ pool.delete_observer(observer)
69
+ end
70
+
71
+ private
72
+
73
+ def setup_filter
74
+ @bloom = Tapyrus::BloomFilter.create_filter(512, 0.01)
75
+ wallet.watch_targets.each{|t|bloom.add(t.htb)} if wallet
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,7 @@
1
+ module Tapyrus
2
+ module Node
3
+ autoload :SPV, 'tapyrus/node/spv'
4
+ autoload :CLI, 'tapyrus/node/cli'
5
+ autoload :Configuration, 'tapyrus/node/configuration'
6
+ end
7
+ end
@@ -0,0 +1,178 @@
1
+ module Tapyrus
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_NOP2 = OP_CHECKLOCKTIMEVERIFY = OP_CLTV = 0xb1
120
+ OP_NOP3 = OP_CHECKSEQUENCEVERIFY = OP_CSV = 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
+ # https://en.bitcoin.it/wiki/Script#Pseudo-words
140
+ OP_PUBKEYHASH = 0xfd
141
+ OP_PUBKEY = 0xfe
142
+ OP_INVALIDOPCODE = 0xff
143
+
144
+ DUPLICATE_KEY = [:OP_NOP2, :OP_NOP3]
145
+ OPCODES_MAP = Hash[*(constants.grep(/^OP_/) - [:OP_NOP2, :OP_NOP3, :OP_CHECKLOCKTIMEVERIFY, :OP_CHECKSEQUENCEVERIFY]).map { |c| [const_get(c), c.to_s] }.flatten]
146
+ NAME_MAP = Hash[*constants.grep(/^OP_/).map { |c| [c.to_s, const_get(c)] }.flatten]
147
+
148
+ def opcode_to_name(opcode)
149
+ return OPCODES_MAP[opcode].delete('OP_') if opcode == OP_0 || (opcode <= OP_16 && opcode >= OP_1)
150
+ OPCODES_MAP[opcode]
151
+ end
152
+
153
+ def name_to_opcode(name)
154
+ return NAME_MAP['OP_' + name] if name =~ /^\d/ && name.to_i < 17 && name.to_i > -1
155
+ NAME_MAP[name]
156
+ end
157
+
158
+ # whether opcode is predefined opcode
159
+ def defined?(opcode)
160
+ !opcode_to_name(opcode).nil?
161
+ end
162
+
163
+ def small_int_to_opcode(int)
164
+ return OP_0 if int == 0
165
+ return OP_1NEGATE if int == -1
166
+ return OP_1 + (int - 1) if int >= 1 && int <= 16
167
+ nil
168
+ end
169
+
170
+ def opcode_to_small_int(opcode)
171
+ return 0 if opcode == ''.b || opcode == OP_0
172
+ return -1 if opcode == OP_1NEGATE
173
+ return opcode - (OP_1 - 1) if opcode >= OP_1 && opcode <= OP_16
174
+ nil
175
+ end
176
+
177
+ end
178
+ end
@@ -0,0 +1,44 @@
1
+ module Tapyrus
2
+
3
+ # outpoint class
4
+ class OutPoint
5
+
6
+ COINBASE_HASH = '0000000000000000000000000000000000000000000000000000000000000000'
7
+ COINBASE_INDEX = 4294967295
8
+
9
+ attr_reader :tx_hash
10
+ attr_reader :index
11
+
12
+ def initialize(tx_hash, index = -1)
13
+ @tx_hash = tx_hash
14
+ @index = index
15
+ end
16
+
17
+ def self.from_txid(txid, index)
18
+ self.new(txid.rhex, index)
19
+ end
20
+
21
+ def coinbase?
22
+ tx_hash == COINBASE_HASH && index == COINBASE_INDEX
23
+ end
24
+
25
+ def to_payload
26
+ [tx_hash.htb, index].pack('a32V')
27
+ end
28
+
29
+ def self.create_coinbase_outpoint
30
+ new(COINBASE_HASH, COINBASE_INDEX)
31
+ end
32
+
33
+ def valid?
34
+ index >= 0 && (!coinbase? && tx_hash != COINBASE_HASH)
35
+ end
36
+
37
+ # convert hash to txid
38
+ def txid
39
+ tx_hash.rhex
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,65 @@
1
+ require 'evma_httpserver'
2
+ require 'json'
3
+
4
+ module Tapyrus
5
+ module RPC
6
+
7
+ # Tapyrusrb RPC server.
8
+ class HttpServer < EM::Connection
9
+ include EM::HttpServer
10
+ include RequestHandler
11
+
12
+ attr_reader :node
13
+ attr_accessor :logger
14
+
15
+ def initialize(node)
16
+ @node = node
17
+ @logger = Tapyrus::Logger.create(:debug)
18
+ end
19
+
20
+ def post_init
21
+ super
22
+ logger.debug 'start http server.'
23
+ end
24
+
25
+ def self.run(node, port = 8332)
26
+ EM.start_server('0.0.0.0', port, HttpServer, node)
27
+ end
28
+
29
+ # process http request.
30
+ def process_http_request
31
+ operation = proc {
32
+ command, args = parse_json_params
33
+ logger.debug("process http request. command = #{command}")
34
+ begin
35
+ send(command, *args).to_json
36
+ rescue Exception => e
37
+ e
38
+ end
39
+ }
40
+ callback = proc{ |result|
41
+ response = EM::DelegatedHttpResponse.new(self)
42
+ if result.is_a?(Exception)
43
+ response.status = 500
44
+ response.content = result.message
45
+ else
46
+ response.status = 200
47
+ response.content = result
48
+ end
49
+ response.content_type 'application/json'
50
+ response.send_response
51
+ }
52
+ EM.defer(operation, callback)
53
+ end
54
+
55
+ # parse request parameter.
56
+ # @return [Array] the array of command and args
57
+ def parse_json_params
58
+ params = JSON.parse(@http_post_content)
59
+ [params['method'], params['params']]
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end