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,93 @@
1
+ module Tapyrus
2
+ module SLIP39
3
+
4
+ WORDS = File.readlines("#{__dir__}/slip39/wordlist/english.txt").map(&:strip)
5
+
6
+ module_function
7
+
8
+ def bits_to_bytes(n)
9
+ (n + 7) / 8
10
+ end
11
+
12
+ def bits_to_words(n)
13
+ (n + RADIX_BITS - 1) / RADIX_BITS
14
+ end
15
+
16
+ # The length of the radix in bits.
17
+ RADIX_BITS = 10
18
+ # The number of words in the wordlist.
19
+ RADIX = 2 ** RADIX_BITS
20
+ # The length of the random identifier in bits.
21
+ ID_LENGTH_BITS = 15
22
+ # The length of the iteration exponent in bits.
23
+ ITERATION_EXP_LENGTH_BITS = 5
24
+ # The length of the random identifier and iteration exponent in words.
25
+ ID_EXP_LENGTH_WORDS = bits_to_words(ID_LENGTH_BITS + ITERATION_EXP_LENGTH_BITS)
26
+ # The maximum number of shares that can be created.
27
+ MAX_SHARE_COUNT = 16
28
+ # The length of the RS1024 checksum in words.
29
+ CHECKSUM_LENGTH_WORDS = 3
30
+ # The length of the digest of the shared secret in bytes.
31
+ DIGEST_LENGTH_BYTES = 4
32
+ # The customization string used in the RS1024 checksum and in the PBKDF2 salt.
33
+ CUSTOMIZATION_STRING = 'shamir'.bytes
34
+ # The length of the mnemonic in words without the share value.
35
+ METADATA_LENGTH_WORDS = ID_EXP_LENGTH_WORDS + 2 + CHECKSUM_LENGTH_WORDS
36
+ # The minimum allowed entropy of the master secret.
37
+ MIN_STRENGTH_BITS = 128
38
+ # The minimum allowed length of the mnemonic in words.
39
+ MIN_MNEMONIC_LENGTH_WORDS = METADATA_LENGTH_WORDS + bits_to_words(MIN_STRENGTH_BITS)
40
+ # The minimum number of iterations to use in PBKDF2.
41
+ BASE_ITERATION_COUNT = 10000
42
+ # The number of rounds to use in the Feistel cipher.
43
+ ROUND_COUNT = 4
44
+ # The index of the share containing the shared secret.
45
+ SECRET_INDEX = 255
46
+ # The index of the share containing the digest of the shared secret.
47
+ DIGEST_INDEX = 254
48
+
49
+ EXP_TABLE = [
50
+ 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19,
51
+ 53, 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34,
52
+ 102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144,
53
+ 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184,
54
+ 211, 110, 178, 205, 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241,
55
+ 8, 24, 40, 120, 136, 131, 158, 185, 208, 107, 189, 220, 127, 129, 152,
56
+ 179, 206, 73, 219, 118, 154, 181, 196, 87, 249, 16, 48, 80, 240, 11,
57
+ 29, 39, 105, 187, 214, 97, 163, 254, 25, 43, 125, 135, 146, 173, 236,
58
+ 47, 113, 147, 174, 233, 32, 96, 160, 251, 22, 58, 78, 210, 109, 183,
59
+ 194, 93, 231, 50, 86, 250, 21, 63, 65, 195, 94, 226, 61, 71, 201,
60
+ 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, 159, 186, 213, 100, 172,
61
+ 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, 155, 182, 193, 88,
62
+ 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, 252, 31, 33,
63
+ 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, 69, 207,
64
+ 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, 18,
65
+ 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
66
+ 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246
67
+ ]
68
+
69
+ LOG_TABLE = [
70
+ 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3,
71
+ 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28,
72
+ 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201,
73
+ 9, 120, 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53,
74
+ 147, 218, 142, 150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241,
75
+ 64, 70, 131, 56, 102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226,
76
+ 152, 34, 136, 145, 16, 126, 110, 72, 195, 163, 182, 30, 66, 58, 107,
77
+ 40, 84, 250, 133, 61, 186, 43, 121, 10, 21, 155, 159, 94, 202, 78,
78
+ 212, 172, 229, 243, 115, 167, 87, 175, 88, 168, 80, 244, 234, 214, 116,
79
+ 79, 174, 233, 213, 231, 230, 173, 232, 44, 215, 117, 122, 235, 22, 11,
80
+ 245, 89, 203, 95, 176, 156, 169, 81, 160, 127, 12, 246, 111, 23, 196,
81
+ 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, 204, 187, 62, 90, 251,
82
+ 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157, 151, 178, 135, 144,
83
+ 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209, 83, 57, 132,
84
+ 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, 68, 17, 146,
85
+ 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237,
86
+ 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7
87
+ ]
88
+
89
+ autoload :SSS, 'tapyrus/slip39/sss'
90
+ autoload :Share, 'tapyrus/slip39/share'
91
+
92
+ end
93
+ end
@@ -0,0 +1,67 @@
1
+ module Tapyrus
2
+ module Store
3
+
4
+ # wrap a block header object with extra data.
5
+ class ChainEntry
6
+
7
+ attr_reader :header
8
+ attr_reader :height
9
+
10
+ # @param [Tapyrus::BlockHeader] header a block header.
11
+ # @param [Integer] height a block height.
12
+ def initialize(header, height)
13
+ @header = header
14
+ @height = height
15
+ end
16
+
17
+ # get database key
18
+ def key
19
+ Tapyrus::Store::KEY_PREFIX[:entry] + header.block_hash
20
+ end
21
+
22
+ def hash
23
+ header.hash
24
+ end
25
+
26
+ # block hash
27
+ def block_hash
28
+ header.block_hash
29
+ end
30
+
31
+ # previous block hash
32
+ def prev_hash
33
+ header.prev_hash
34
+ end
35
+
36
+ # whether genesis block
37
+ def genesis?
38
+ Tapyrus.chain_params.genesis_block.header == header
39
+ end
40
+
41
+ # @param [String] payload a payload with binary format.
42
+ def self.parse_from_payload(payload)
43
+ buf = StringIO.new(payload)
44
+ len = Tapyrus.unpack_var_int_from_io(buf)
45
+ height = buf.read(len).reverse.bth.to_i(16)
46
+ new(Tapyrus::BlockHeader.parse_from_payload(buf.read(80)), height)
47
+ end
48
+
49
+ # build next block +StoredBlock+ instance.
50
+ # @param [Tapyrus::BlockHeader] next_block a next block candidate header.
51
+ # @return [Tapyrus::Store::ChainEntry] a next stored block (not saved).
52
+ def build_next_block(next_block)
53
+ ChainEntry.new(next_block, height + 1)
54
+ end
55
+
56
+ # generate payload
57
+ def to_payload
58
+ height_value = height.to_even_length_hex
59
+ height_value = height_value.htb.reverse
60
+ Tapyrus.pack_var_int(height_value.bytesize) + height_value + header.to_payload
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,98 @@
1
+ require 'leveldb-native'
2
+
3
+ module Tapyrus
4
+ module Store
5
+ module DB
6
+
7
+ class LevelDB
8
+
9
+ attr_reader :db
10
+ attr_reader :logger
11
+
12
+ def initialize(path = "#{Tapyrus.base_dir}/db/spv")
13
+ # @logger = Tapyrus::Logger.create(:debug)
14
+ FileUtils.mkdir_p(path)
15
+ @db = ::LevelDBNative::DB.new(path)
16
+ # logger.debug 'Opened LevelDB successfully.'
17
+ end
18
+
19
+ # put data into LevelDB.
20
+ # @param [Object] key a key.
21
+ # @param [Object] value a value.
22
+ def put(key, value)
23
+ # logger.debug "put #{key} data"
24
+ db.put(key, value)
25
+ end
26
+
27
+ # get value from specified key.
28
+ # @param [Object] key a key.
29
+ # @return[Object] the stored value.
30
+ def get(key)
31
+ db.get(key)
32
+ end
33
+
34
+ # get best block hash.
35
+ def best_hash
36
+ db.get(KEY_PREFIX[:best])
37
+ end
38
+
39
+ # delete specified key data.
40
+ def delete(key)
41
+ db.delete(key)
42
+ end
43
+
44
+ # get block hash specified +height+
45
+ def get_hash_from_height(height)
46
+ db.get(height_key(height))
47
+ end
48
+
49
+ # get next block hash specified +hash+
50
+ def next_hash(hash)
51
+ db.get(KEY_PREFIX[:next] + hash)
52
+ end
53
+
54
+ # get entry payload
55
+ # @param [String] hash the hash with hex format.
56
+ # @return [String] the ChainEntry payload.
57
+ def get_entry_payload_from_hash(hash)
58
+ db.get(KEY_PREFIX[:entry] + hash)
59
+ end
60
+
61
+ def save_entry(entry)
62
+ db.batch do
63
+ db.put(entry.key ,entry.to_payload)
64
+ db.put(height_key(entry.height), entry.block_hash)
65
+ connect_entry(entry)
66
+ end
67
+ end
68
+
69
+ def close
70
+ db.close
71
+ end
72
+
73
+ private
74
+
75
+ # generate height key
76
+ def height_key(height)
77
+ height = height.to_even_length_hex
78
+ KEY_PREFIX[:height] + height.rhex
79
+ end
80
+
81
+ def connect_entry(entry)
82
+ unless entry.genesis?
83
+ tip_block = Tapyrus::Store::ChainEntry.parse_from_payload(get_entry_payload_from_hash(best_hash))
84
+ unless tip_block.block_hash == entry.prev_hash
85
+ raise "entry(#{entry.block_hash}) does not reference current best block hash(#{tip_block.block_hash})"
86
+ end
87
+ unless tip_block.height + 1 == entry.height
88
+ raise "block height is small than current best block."
89
+ end
90
+ end
91
+ db.put(KEY_PREFIX[:best], entry.block_hash)
92
+ db.put(KEY_PREFIX[:next] + entry.prev_hash, entry.block_hash)
93
+ end
94
+ end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ module Tapyrus
2
+ module Store
3
+
4
+ module DB
5
+ autoload :LevelDB, 'tapyrus/store/db/level_db'
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,101 @@
1
+ module Tapyrus
2
+
3
+ module Store
4
+
5
+ KEY_PREFIX = {
6
+ entry: 'e', # key: block hash, value: Tapyrus::Store::ChainEntry payload
7
+ height: 'h', # key: block height, value: block hash.
8
+ best: 'B', # value: best block hash.
9
+ next: 'n' # key: block hash, value: A hash of the next block of the specified hash
10
+ }
11
+
12
+ class SPVChain
13
+
14
+ attr_reader :db
15
+ attr_reader :logger
16
+
17
+ def initialize(db = Tapyrus::Store::DB::LevelDB.new)
18
+ @db = db # TODO multiple db switch
19
+ @logger = Tapyrus::Logger.create(:debug)
20
+ initialize_block
21
+ end
22
+
23
+ # get latest block in the store.
24
+ # @return[Tapyrus::Store::ChainEntry]
25
+ def latest_block
26
+ hash = db.best_hash
27
+ return nil unless hash
28
+ find_entry_by_hash(hash)
29
+ end
30
+
31
+ # find block entry with the specified height.
32
+ def find_entry_by_height(height)
33
+ find_entry_by_hash(db.get_hash_from_height(height))
34
+ end
35
+
36
+ # find block entry with the specified hash
37
+ def find_entry_by_hash(hash)
38
+ payload = db.get_entry_payload_from_hash(hash)
39
+ return nil unless payload
40
+ ChainEntry.parse_from_payload(payload)
41
+ end
42
+
43
+ # append block header to chain.
44
+ # @param [Tapyrus::BlockHeader] header a block header.
45
+ # @return [Tapyrus::Store::ChainEntry] appended block header entry.
46
+ def append_header(header)
47
+ logger.info("append header #{header.block_id}")
48
+ raise "this header is invalid. #{header.block_hash}" unless header.valid?
49
+ best_block = latest_block
50
+ current_height = best_block.height
51
+ if best_block.block_hash == header.prev_hash
52
+ entry = Tapyrus::Store::ChainEntry.new(header, current_height + 1)
53
+ db.save_entry(entry)
54
+ entry
55
+ else
56
+ unless find_entry_by_hash(header.block_hash)
57
+ # TODO implements recovery process
58
+ raise "header's previous hash(#{header.prev_hash}) does not match current best block's(#{best_block.block_hash})."
59
+ end
60
+ end
61
+ end
62
+
63
+ # get next block hash for specified +hash+
64
+ # @param [String] hash the block hash(little endian)
65
+ # @return [String] the next block hash. If it does not exist yet, return nil.
66
+ def next_hash(hash)
67
+ db.next_hash(hash)
68
+ end
69
+
70
+ # get median time past for specified block +hash+
71
+ # @param [String] hash the block hash.
72
+ # @return [Integer] the median time past value.
73
+ def mtp(hash)
74
+ time = []
75
+ Tapyrus::MEDIAN_TIME_SPAN.times do
76
+ entry = find_entry_by_hash(hash)
77
+ break unless entry
78
+
79
+ time << entry.header.time
80
+ hash = entry.header.prev_hash
81
+ end
82
+ time.sort!
83
+ time[time.size / 2]
84
+ end
85
+
86
+ private
87
+
88
+ # if database is empty, put genesis block.
89
+ def initialize_block
90
+ unless latest_block
91
+ block = Tapyrus.chain_params.genesis_block
92
+ genesis = ChainEntry.new(block.header, 0)
93
+ db.save_entry(genesis)
94
+ end
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,9 @@
1
+ module Tapyrus
2
+ module Store
3
+
4
+ autoload :DB, 'tapyrus/store/db'
5
+ autoload :SPVChain, 'tapyrus/store/spv_chain'
6
+ autoload :ChainEntry, 'tapyrus/store/chain_entry'
7
+
8
+ end
9
+ end