tapyrus 0.1.0

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.
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