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,78 @@
1
+ require "murmurhash3"
2
+ module Tapyrus
3
+ class BloomFilter
4
+ LN2_SQUARED = 0.4804530139182014246671025263266649717305529515945455 # log(2) ** 2
5
+ LN2 = 0.6931471805599453094172321214581765680755001343602552 # log(2)
6
+
7
+ MAX_BLOOM_FILTER_SIZE = 36_000 # bytes
8
+ MAX_HASH_FUNCS = 50
9
+
10
+ attr_reader :filter, :hash_funcs, :tweak
11
+
12
+ def initialize(filter, hash_funcs, tweak)
13
+ @filter = filter
14
+ @hash_funcs = hash_funcs
15
+ @tweak = tweak
16
+ end
17
+
18
+ # Create a new bloom filter.
19
+ # @param [Integer] elements_length the number of elements
20
+ # @param [Float] fp_rate the false positive rate chosen by the client
21
+ # @param [Integer] tweak A random value to add to the seed value in the hash function used by the bloom filter
22
+ def self.create_filter(elements_length, fp_rate, tweak = 0)
23
+ # The size S of the filter in bytes is given by (-1 / pow(log(2), 2) * N * log(P)) / 8
24
+ len = [[(-elements_length * Math.log(fp_rate) / (LN2_SQUARED * 8)).to_i, MAX_BLOOM_FILTER_SIZE].min, 1].max
25
+ filter = Array.new(len, 0)
26
+ # The number of hash functions required is given by S * 8 / N * log(2)
27
+ hash_funcs = [[(filter.size * 8 * LN2 / elements_length).to_i, MAX_HASH_FUNCS].min, 1].max
28
+ BloomFilter.new(filter, hash_funcs, tweak)
29
+ end
30
+
31
+ # @param [String] data The data element to add to the current filter.
32
+ def add(data)
33
+ return if full?
34
+ hash_funcs.times do |i|
35
+ hash = to_hash(data, i)
36
+ set_bit(hash)
37
+ end
38
+ end
39
+
40
+ # Returns true if the given data matches the filter
41
+ # @param [String] data The data to check the current filter
42
+ # @return [Boolean] true if the given data matches the filter
43
+ def contains?(data)
44
+ return true if full?
45
+ hash_funcs.times do |i|
46
+ hash = to_hash(data, i)
47
+ return false unless check_bit(hash)
48
+ end
49
+ true
50
+ end
51
+
52
+ def clear
53
+ filter.fill(0)
54
+ @full = false
55
+ end
56
+
57
+ def to_a
58
+ filter
59
+ end
60
+
61
+ private
62
+ def to_hash(data, i)
63
+ MurmurHash3::V32.str_hash(data, (i * 0xfba4c795 + tweak) & 0xffffffff) % (filter.length * 8)
64
+ end
65
+
66
+ def set_bit(data)
67
+ filter[data >> 3] |= (1 << (7 & data))
68
+ end
69
+
70
+ def check_bit(data)
71
+ filter[data >> 3] & (1 << (7 & data)) != 0
72
+ end
73
+
74
+ def full?
75
+ @full |= filter.all? {|byte| byte == 0xff}
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,90 @@
1
+ require 'yaml'
2
+
3
+ module Tapyrus
4
+
5
+ # Network parameter class
6
+ class ChainParams
7
+
8
+ attr_reader :network
9
+ attr_reader :magic_head
10
+ attr_reader :message_magic
11
+ attr_reader :address_version
12
+ attr_reader :p2sh_version
13
+ attr_reader :bech32_hrp
14
+ attr_reader :privkey_version
15
+ attr_reader :extended_privkey_version
16
+ attr_reader :extended_pubkey_version
17
+ attr_reader :bip49_pubkey_p2wpkh_p2sh_version
18
+ attr_reader :bip49_privkey_p2wpkh_p2sh_version
19
+ attr_reader :bip49_pubkey_p2wsh_p2sh_version
20
+ attr_reader :bip49_privkey_p2wsh_p2sh_version
21
+ attr_reader :bip84_pubkey_p2wpkh_version
22
+ attr_reader :bip84_privkey_p2wpkh_version
23
+ attr_reader :bip84_pubkey_p2wsh_version
24
+ attr_reader :bip84_privkey_p2wsh_version
25
+ attr_reader :default_port
26
+ attr_reader :protocol_version
27
+ attr_reader :retarget_interval
28
+ attr_reader :retarget_time
29
+ attr_reader :target_spacing
30
+ attr_reader :max_money
31
+ attr_reader :bip34_height
32
+ attr_reader :proof_of_work_limit
33
+ attr_reader :dns_seeds
34
+ attr_reader :genesis
35
+ attr_reader :bip44_coin_type
36
+
37
+ attr_accessor :dust_relay_fee
38
+
39
+ # fork coin id.
40
+ attr_accessor :fork_id
41
+
42
+ # mainnet genesis
43
+ def self.mainnet
44
+ init('mainnet')
45
+ end
46
+
47
+ # testnet genesis
48
+ def self.testnet
49
+ init('testnet')
50
+ end
51
+
52
+ # regtest genesis
53
+ def self.regtest
54
+ init('regtest')
55
+ end
56
+
57
+ def mainnet?
58
+ network == 'mainnet'
59
+ end
60
+
61
+ def testnet?
62
+ network == 'testnet'
63
+ end
64
+
65
+ def regtest?
66
+ network == 'regtest'
67
+ end
68
+
69
+ def genesis_block
70
+ header = Tapyrus::BlockHeader.new(
71
+ genesis['version'], genesis['prev_hash'].rhex, genesis['merkle_root'].rhex,
72
+ genesis['time'], genesis['bits'], genesis['nonce'])
73
+ Tapyrus::Block.new(header)
74
+ end
75
+
76
+ # whether fork coin.
77
+ def fork_chain?
78
+ !fork_id.nil?
79
+ end
80
+
81
+ def self.init(name)
82
+ i = YAML.load(File.open("#{__dir__}/chainparams/#{name}.yml"))
83
+ i.dust_relay_fee ||= Tapyrus::DUST_RELAY_TX_FEE
84
+ i
85
+ end
86
+
87
+ private_class_method :init
88
+ end
89
+
90
+ end
@@ -0,0 +1,41 @@
1
+ --- !ruby/object:Tapyrus::ChainParams
2
+ network: "mainnet"
3
+ magic_head: "f9beb4d9"
4
+ message_magic: "Bitcoin Signed Message:\n"
5
+ address_version: "00"
6
+ p2sh_version: "05"
7
+ bech32_hrp: 'bc'
8
+ privkey_version: "80"
9
+ extended_privkey_version: "0488ade4"
10
+ extended_pubkey_version: "0488b21e"
11
+ bip49_pubkey_p2wpkh_p2sh_version: "049d7cb2"
12
+ bip49_pubkey_p2wsh_p2sh_version: "0295b43f"
13
+ bip49_privkey_p2wpkh_p2sh_version: "049d7878"
14
+ bip49_privkey_p2wsh_p2sh_version: "0295b005"
15
+ bip84_pubkey_p2wpkh_version: "04b24746"
16
+ bip84_pubkey_p2wsh_version: "02aa7ed3"
17
+ bip84_privkey_p2wpkh_version: "04b2430c"
18
+ bip84_privkey_p2wsh_version: "02aa7a99"
19
+ default_port: 8333
20
+ protocol_version: 70013
21
+ retarget_interval: 2016
22
+ retarget_time: 1209600 # 2 weeks
23
+ target_spacing: 600 # block interval
24
+ max_money: 21000000
25
+ bip34_height: 227931
26
+ proof_of_work_limit: 0x1d00ffff
27
+ dns_seeds:
28
+ - "seed.bitcoin.sipa.be"
29
+ - "dnsseed.bluematt.me"
30
+ - "dnsseed.bitcoin.dashjr.org"
31
+ - "seed.bitcoinstats.com"
32
+ - "seed.bitcoin.jonasschnelli.ch"
33
+ genesis:
34
+ hash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
35
+ merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
36
+ time: 1231006505
37
+ nonce: 2083236893
38
+ bits: 0x1d00ffff
39
+ version: 1
40
+ prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
41
+ bip44_coin_type: 0
@@ -0,0 +1,38 @@
1
+ --- !ruby/object:Tapyrus::ChainParams
2
+ network: "regtest"
3
+ magic_head: "fabfb5da"
4
+ message_magic: "Bitcoin Signed Message:\n"
5
+ address_version: "6f"
6
+ p2sh_version: "c4"
7
+ bech32_hrp: 'bcrt'
8
+ privkey_version: "ef"
9
+ extended_privkey_version: "04358394"
10
+ extended_pubkey_version: "043587cf"
11
+ bip49_pubkey_p2wpkh_p2sh_version: "044a5262"
12
+ bip49_pubkey_p2wsh_p2sh_version: "024285ef"
13
+ bip49_privkey_p2wpkh_p2sh_version: "044a4e28"
14
+ bip49_privkey_p2wsh_p2sh_version: "024285b5"
15
+ bip84_pubkey_p2wpkh_version: "045f1cf6"
16
+ bip84_pubkey_p2wsh_version: "02575483"
17
+ bip84_privkey_p2wpkh_version: "045f18bc"
18
+ bip84_privkey_p2wsh_version: "02575048"
19
+ default_port: 18444
20
+ protocol_version: 70013
21
+ retarget_interval: 2016
22
+ retarget_time: 1209600 # 2 weeks
23
+ target_spacing: 600 # block interval
24
+ max_money: 21000000
25
+ bip34_height: 0
26
+ genesis_hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
27
+ proof_of_work_limit: 0x207fffff
28
+ dns_seeds:
29
+ genesis:
30
+ hash: "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"
31
+ merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
32
+ time: 1296688602
33
+ nonce: 2
34
+ bits: 0x207fffff
35
+ version: 1
36
+ prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
37
+ bip44_coin_type: 1
38
+ dust_relay_fee: 3600
@@ -0,0 +1,41 @@
1
+ --- !ruby/object:Tapyrus::ChainParams
2
+ network: "testnet"
3
+ magic_head: "0b110907"
4
+ message_magic: "Bitcoin Signed Message:\n"
5
+ address_version: "6f"
6
+ p2sh_version: "c4"
7
+ bech32_hrp: 'tb'
8
+ privkey_version: "ef"
9
+ extended_privkey_version: "04358394"
10
+ extended_pubkey_version: "043587cf"
11
+ bip49_pubkey_p2wpkh_p2sh_version: "044a5262"
12
+ bip49_pubkey_p2wsh_p2sh_version: "024285ef"
13
+ bip49_privkey_p2wpkh_p2sh_version: "044a4e28"
14
+ bip49_privkey_p2wsh_p2sh_version: "024285b5"
15
+ bip84_pubkey_p2wpkh_version: "045f1cf6"
16
+ bip84_pubkey_p2wsh_version: "02575483"
17
+ bip84_privkey_p2wpkh_version: "045f18bc"
18
+ bip84_privkey_p2wsh_version: "02575048"
19
+ default_port: 18333
20
+ protocol_version: 70013
21
+ retarget_interval: 2016
22
+ retarget_time: 1209600 # 2 weeks
23
+ target_spacing: 600 # block interval
24
+ max_money: 21000000
25
+ bip34_height: 227931
26
+ genesis_hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
27
+ proof_of_work_limit: 0x1d00ffff
28
+ dns_seeds:
29
+ - "testnet-seed.bitcoin.jonasschnelli.ch"
30
+ - "seed.tbtc.petertodd.org"
31
+ - "testnet-seed.bluematt.me"
32
+ - "testnet-seed.bitcoin.schildbach.de"
33
+ genesis:
34
+ hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
35
+ merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"
36
+ time: 1296688602
37
+ nonce: 414098458
38
+ bits: 0x1d00ffff
39
+ version: 1
40
+ prev_hash: "0000000000000000000000000000000000000000000000000000000000000000"
41
+ bip44_coin_type: 1
@@ -0,0 +1,195 @@
1
+ module Tapyrus
2
+
3
+ COIN = 100_000_000
4
+ MAX_MONEY = 21_000_000 * COIN
5
+
6
+ # The maximum allowed size for a serialized block, in bytes (only for buffer size limits)
7
+ MAX_BLOCK_SERIALIZED_SIZE = 4_000_000
8
+ # The maximum allowed weight for a block, see BIP 141 (network rule)
9
+ MAX_BLOCK_WEIGHT = 4_000_000
10
+ # The maximum allowed number of signature check operations in a block (network rule)
11
+ MAX_BLOCK_SIGOPS_COST = 80_000
12
+ # Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
13
+ COINBASE_MATURITY = 100
14
+ WITNESS_SCALE_FACTOR = 4
15
+
16
+ # 60 is the lower bound for the size of a valid serialized Tx
17
+ MIN_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 60
18
+ # 10 is the lower bound for the size of a serialized Tx
19
+ MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * 10
20
+
21
+ # Flags for nSequence and nLockTime locks
22
+ LOCKTIME_VERIFY_SEQUENCE = (1 << 0)
23
+ LOCKTIME_MEDIAN_TIME_PAST = (1 << 1)
24
+
25
+ # Min feerate for defining dust.
26
+ DUST_RELAY_TX_FEE = 3000
27
+
28
+ # script verify flags
29
+ SCRIPT_VERIFY_NONE = 0
30
+ SCRIPT_VERIFY_P2SH = (1 << 0)
31
+ SCRIPT_VERIFY_STRICTENC = (1 << 1)
32
+ SCRIPT_VERIFY_DERSIG = (1 << 2)
33
+ SCRIPT_VERIFY_LOW_S = (1 << 3)
34
+ SCRIPT_VERIFY_NULLDUMMY = (1 << 4)
35
+ SCRIPT_VERIFY_SIGPUSHONLY = (1 << 5)
36
+ SCRIPT_VERIFY_MINIMALDATA = (1 << 6)
37
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7)
38
+ SCRIPT_VERIFY_CLEANSTACK = (1 << 8)
39
+ SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1 << 9) # Verify CHECKLOCKTIMEVERIFY (BIP-65)
40
+ SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1 << 10) # support CHECKSEQUENCEVERIFY opcode (BIP-112)
41
+ SCRIPT_VERIFY_WITNESS = (1 << 11) # Support segregated witness
42
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1 << 12) # Making v1-v16 witness program non-standard
43
+ SCRIPT_VERIFY_MINIMALIF = (1 << 13) # Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
44
+ SCRIPT_VERIFY_NULLFAIL = (1 << 14) # Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
45
+ SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1 << 15) # Public keys in segregated witness scripts must be compressed
46
+ SCRIPT_VERIFY_CONST_SCRIPTCODE = (1 << 16) # Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts
47
+
48
+ MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH
49
+
50
+ # Standard script verification flags that standard transactions will comply with.
51
+ STANDARD_SCRIPT_VERIFY_FLAGS = [MANDATORY_SCRIPT_VERIFY_FLAGS,
52
+ SCRIPT_VERIFY_DERSIG,
53
+ SCRIPT_VERIFY_STRICTENC,
54
+ SCRIPT_VERIFY_MINIMALDATA,
55
+ SCRIPT_VERIFY_NULLDUMMY,
56
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS,
57
+ SCRIPT_VERIFY_CLEANSTACK,
58
+ SCRIPT_VERIFY_MINIMALIF,
59
+ SCRIPT_VERIFY_NULLFAIL,
60
+ SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY,
61
+ SCRIPT_VERIFY_CHECKSEQUENCEVERIFY,
62
+ SCRIPT_VERIFY_LOW_S,
63
+ SCRIPT_VERIFY_WITNESS,
64
+ SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM,
65
+ SCRIPT_VERIFY_WITNESS_PUBKEYTYPE,
66
+ SCRIPT_VERIFY_CONST_SCRIPTCODE].inject(SCRIPT_VERIFY_NONE){|flags, f| flags |= f}
67
+
68
+ # for script
69
+
70
+ # witness version
71
+ WITNESS_VERSION = 0x00
72
+
73
+ # Maximum script length in bytes
74
+ MAX_SCRIPT_SIZE = 10000
75
+
76
+ # Maximum number of public keys per multisig
77
+ MAX_PUBKEYS_PER_MULTISIG = 20
78
+
79
+ # Maximum number of non-push operations per script
80
+ MAX_OPS_PER_SCRIPT = 201
81
+
82
+ # Maximum number of bytes pushable to the stack
83
+ MAX_SCRIPT_ELEMENT_SIZE = 520
84
+
85
+ # Maximum number of size in the stack
86
+ MAX_STACK_SIZE = 1000
87
+
88
+ # Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
89
+ LOCKTIME_THRESHOLD = 500000000
90
+
91
+ # Signature hash types/flags
92
+ SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
93
+
94
+ # SIGHASH_FORK_ID for replay protection of the fork coin
95
+ SIGHASH_FORK_ID = 0x40
96
+
97
+ # fork coin id.
98
+ FORK_ID_CASH = 0
99
+ FORK_ID_GOLD = 79
100
+
101
+ # Maximum number length in bytes
102
+ DEFAULT_MAX_NUM_SIZE = 4
103
+
104
+ # 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
105
+ MAX_OP_RETURN_RELAY = 83
106
+
107
+ SIG_VERSION = [:base, :witness_v0]
108
+
109
+ # for script error
110
+ SCRIPT_ERR_OK = 0
111
+ SCRIPT_ERR_UNKNOWN_ERROR = 1
112
+ SCRIPT_ERR_EVAL_FALSE = 2
113
+ SCRIPT_ERR_OP_RETURN = 3
114
+
115
+ # Max sizes
116
+ SCRIPT_ERR_SCRIPT_SIZE = 10
117
+ SCRIPT_ERR_PUSH_SIZE = 11
118
+ SCRIPT_ERR_OP_COUNT = 12
119
+ SCRIPT_ERR_STACK_SIZE = 13
120
+ SCRIPT_ERR_SIG_COUNT = 14
121
+ SCRIPT_ERR_PUBKEY_COUNT = 15
122
+
123
+ # Failed verify operations
124
+ SCRIPT_ERR_VERIFY = 20
125
+ SCRIPT_ERR_EQUALVERIFY = 21
126
+ SCRIPT_ERR_CHECKMULTISIGVERIFY = 22
127
+ SCRIPT_ERR_CHECKSIGVERIFY = 23
128
+ SCRIPT_ERR_NUMEQUALVERIFY = 24
129
+
130
+ # Logical/Format/Canonical errors
131
+ SCRIPT_ERR_BAD_OPCODE = 30
132
+ SCRIPT_ERR_DISABLED_OPCODE = 31
133
+ SCRIPT_ERR_INVALID_STACK_OPERATION = 32
134
+ SCRIPT_ERR_INVALID_ALTSTACK_OPERATION = 33
135
+ SCRIPT_ERR_UNBALANCED_CONDITIONAL = 34
136
+
137
+ # CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY
138
+ SCRIPT_ERR_NEGATIVE_LOCKTIME = 40
139
+ SCRIPT_ERR_UNSATISFIED_LOCKTIME = 41
140
+
141
+ # Malleability
142
+ SCRIPT_ERR_SIG_HASHTYPE = 50
143
+ SCRIPT_ERR_SIG_DER = 51
144
+ SCRIPT_ERR_MINIMALDATA = 52
145
+ SCRIPT_ERR_SIG_PUSHONLY = 53
146
+ SCRIPT_ERR_SIG_HIGH_S = 54
147
+ SCRIPT_ERR_SIG_NULLDUMMY = 55
148
+ SCRIPT_ERR_PUBKEYTYPE = 56
149
+ SCRIPT_ERR_CLEANSTACK = 56
150
+ SCRIPT_ERR_MINIMALIF = 57
151
+ SCRIPT_ERR_SIG_NULLFAIL = 58
152
+
153
+ # softfork safeness
154
+ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS = 60
155
+ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = 61
156
+
157
+ # segregated witness
158
+ SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH = 70
159
+ SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY = 71
160
+ SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH = 72
161
+ SCRIPT_ERR_WITNESS_MALLEATED = 73
162
+ SCRIPT_ERR_WITNESS_MALLEATED_P2SH = 74
163
+ SCRIPT_ERR_WITNESS_UNEXPECTED = 75
164
+ SCRIPT_ERR_WITNESS_PUBKEYTYPE = 76
165
+
166
+ # Constant scriptCode
167
+ SCRIPT_ERR_OP_CODESEPARATOR = 77
168
+ SCRIPT_ERR_SIG_FINDANDDELETE = 78
169
+
170
+ SCRIPT_ERR_ERROR_COUNT = 80
171
+
172
+ ERRCODES_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [const_get(c), c.to_s] }.flatten]
173
+ NAME_MAP = Hash[*constants.grep(/^SCRIPT_ERR_/).map { |c| [c.to_s, const_get(c)] }.flatten]
174
+
175
+ # witness commitment
176
+ WITNESS_COMMITMENT_HEADER = 'aa21a9ed'
177
+
178
+ COINBASE_WTXID = '00'* 32
179
+
180
+ # for message
181
+ MESSAGE_HEADER_SIZE = 24
182
+
183
+ # for peer
184
+ PARALLEL_THREAD = 3
185
+
186
+ # Maximum amount of time that a block timestamp is allowed to exceed the current network-adjusted time before the block will be accepted.
187
+ MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60
188
+
189
+ # Size of set to pick median time from.
190
+ MEDIAN_TIME_SPAN = 11
191
+
192
+ BIP32_EXTKEY_WITH_VERSION_SIZE = 78
193
+
194
+ HARDENED_THRESHOLD = 2147483648 # 2**31
195
+ end
@@ -0,0 +1,147 @@
1
+ module Tapyrus
2
+
3
+ module Descriptor
4
+
5
+ include Tapyrus::Opcodes
6
+
7
+ # generate P2PK output for the given public key.
8
+ # @param [String] key private key or public key with hex format
9
+ # @return [Tapyrus::Script] P2PK script.
10
+ def pk(key)
11
+ Tapyrus::Script.new << extract_pubkey(key) << OP_CHECKSIG
12
+ end
13
+
14
+ # generate P2PKH output for the given public key.
15
+ # @param [String] key private key or public key with hex format.
16
+ # @return [Tapyrus::Script] P2PKH script.
17
+ def pkh(key)
18
+ Tapyrus::Script.to_p2pkh(Tapyrus.hash160(extract_pubkey(key)))
19
+ end
20
+
21
+ # generate P2PKH output for the given public key.
22
+ # @param [String] key private key or public key with hex format.
23
+ # @return [Tapyrus::Script] P2WPKH script.
24
+ def wpkh(key)
25
+ pubkey = extract_pubkey(key)
26
+ raise ArgumentError, "Uncompressed key are not allowed." unless compressed_key?(pubkey)
27
+ Tapyrus::Script.to_p2wpkh(Tapyrus.hash160(pubkey))
28
+ end
29
+
30
+ # generate P2SH embed the argument.
31
+ # @param [String or Script] script script to be embed.
32
+ # @return [Tapyrus::Script] P2SH script.
33
+ def sh(script)
34
+ script = script.to_hex if script.is_a?(Tapyrus::Script)
35
+ raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Tapyrus::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.htb.bytesize > Tapyrus::MAX_SCRIPT_ELEMENT_SIZE
36
+ Tapyrus::Script.to_p2sh(Tapyrus.hash160(script))
37
+ end
38
+
39
+ # generate P2WSH embed the argument.
40
+ # @param [String or Script] script script to be embed.
41
+ # @return [Tapyrus::Script] P2WSH script.
42
+ def wsh(script)
43
+ script = Tapyrus::Script(script.htb) if script.is_a?(String)
44
+ raise ArgumentError, "P2SH script is too large, 547 bytes is larger than #{Tapyrus::MAX_SCRIPT_ELEMENT_SIZE} bytes." if script.to_payload.bytesize > Tapyrus::MAX_SCRIPT_ELEMENT_SIZE
45
+ raise ArgumentError, "Uncompressed key are not allowed." if script.get_pubkeys.any?{|p|!compressed_key?(p)}
46
+ Tapyrus::Script.to_p2wsh(script)
47
+ end
48
+
49
+ # an alias for the collection of `pk(KEY)` and `pkh(KEY)`.
50
+ # If the key is compressed, it also includes `wpkh(KEY)` and `sh(wpkh(KEY))`.
51
+ # @param [String] key private key or public key with hex format.
52
+ # @return [Array[Tapyrus::Script]]
53
+ def combo(key)
54
+ result = [pk(key), pkh(key)]
55
+ pubkey = extract_pubkey(key)
56
+ if compressed_key?(pubkey)
57
+ result << wpkh(key)
58
+ result << sh(result.last)
59
+ end
60
+ result
61
+ end
62
+
63
+ # generate multisig output for given keys.
64
+ # @param [Integer] threshold the threshold of multisig.
65
+ # @param [Array[String]] keys an array of keys.
66
+ # @return [Tapyrus::Script] multisig script.
67
+ def multi(threshold, *keys, sort: false)
68
+ raise ArgumentError, 'Multisig threshold is not valid.' unless threshold.is_a?(Integer)
69
+ raise ArgumentError, 'Multisig threshold cannot be 0, must be at least 1.' unless threshold > 0
70
+ raise ArgumentError, 'Multisig threshold cannot be larger than the number of keys.' if threshold > keys.size
71
+ raise ArgumentError, 'Multisig must have between 1 and 16 keys, inclusive.' if keys.size > 16
72
+ pubkeys = keys.map{|key| extract_pubkey(key) }
73
+ Tapyrus::Script.to_multisig_script(threshold, pubkeys, sort: sort)
74
+ end
75
+
76
+ # generate sorted multisig output for given keys.
77
+ # @param [Integer] threshold the threshold of multisig.
78
+ # @param [Array[String]] keys an array of keys.
79
+ # @return [Tapyrus::Script] multisig script.
80
+ def sortedmulti(threshold, *keys)
81
+ multi(threshold, *keys, sort: true)
82
+ end
83
+
84
+ private
85
+
86
+ # extract public key from KEY format.
87
+ # @param [String] key KEY string.
88
+ # @return [String] public key.
89
+ def extract_pubkey(key)
90
+ if key.start_with?('[') # BIP32 fingerprint
91
+ raise ArgumentError, 'Invalid key origin.' if key.count('[') > 1 || key.count(']') > 1
92
+ info = key[1...key.index(']')] # TODO
93
+ fingerprint, *paths = info.split('/')
94
+ raise ArgumentError, 'Fingerprint is not hex.' unless fingerprint.valid_hex?
95
+ raise ArgumentError, 'Fingerprint is not 4 bytes.' unless fingerprint.size == 8
96
+ key = key[(key.index(']') + 1)..-1]
97
+ else
98
+ raise ArgumentError, 'Invalid key origin.' if key.include?(']')
99
+ end
100
+
101
+ # check BIP32 derivation path
102
+ key, *paths = key.split('/')
103
+
104
+ if key.start_with?('xprv')
105
+ key = Tapyrus::ExtKey.from_base58(key)
106
+ key = derive_path(key, paths, true) if paths
107
+ elsif key.start_with?('xpub')
108
+ key = Tapyrus::ExtPubkey.from_base58(key)
109
+ key = derive_path(key, paths, false) if paths
110
+ else
111
+ begin
112
+ key = Tapyrus::Key.from_wif(key)
113
+ rescue ArgumentError
114
+ key_type = compressed_key?(key) ? Tapyrus::Key::TYPES[:compressed] : Tapyrus::Key::TYPES[:uncompressed]
115
+ key = Tapyrus::Key.new(pubkey: key, key_type: key_type)
116
+ end
117
+ end
118
+ key = key.is_a?(Tapyrus::Key) ? key : key.key
119
+ raise ArgumentError, 'Invalid pubkey.' unless key.fully_valid_pubkey?
120
+ key.pubkey
121
+ end
122
+
123
+ def compressed_key?(key)
124
+ %w(02 03).include?(key[0..1]) && [key].pack("H*").bytesize == 33
125
+ end
126
+
127
+ def derive_path(key, paths, is_private)
128
+ paths.each do |path|
129
+ raise ArgumentError, 'xpub can not derive hardened key.' if !is_private && path.end_with?("'")
130
+ if is_private
131
+ hardened = path.end_with?("'")
132
+ path = hardened ? path[0..-2] : path
133
+ raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
134
+ raise ArgumentError, 'Key path value is out of range.' if !hardened && path.to_i >= Tapyrus::HARDENED_THRESHOLD
135
+ key = key.derive(path.to_i, hardened)
136
+ else
137
+ raise ArgumentError, 'Key path value is not a valid value.' unless path =~ /^[0-9]+$/
138
+ raise ArgumentError, 'Key path value is out of range.' if path.to_i >= Tapyrus::HARDENED_THRESHOLD
139
+ key = key.derive(path.to_i)
140
+ end
141
+ end
142
+ key
143
+ end
144
+
145
+ end
146
+
147
+ end