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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +100 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/tapyrusrb-cli +5 -0
- data/exe/tapyrusrbd +41 -0
- data/lib/openassets/marker_output.rb +20 -0
- data/lib/openassets/payload.rb +54 -0
- data/lib/openassets/util.rb +28 -0
- data/lib/openassets.rb +9 -0
- data/lib/tapyrus/base58.rb +38 -0
- data/lib/tapyrus/block.rb +77 -0
- data/lib/tapyrus/block_header.rb +88 -0
- data/lib/tapyrus/bloom_filter.rb +78 -0
- data/lib/tapyrus/chain_params.rb +90 -0
- data/lib/tapyrus/chainparams/mainnet.yml +41 -0
- data/lib/tapyrus/chainparams/regtest.yml +38 -0
- data/lib/tapyrus/chainparams/testnet.yml +41 -0
- data/lib/tapyrus/constants.rb +195 -0
- data/lib/tapyrus/descriptor.rb +147 -0
- data/lib/tapyrus/ext_key.rb +337 -0
- data/lib/tapyrus/key.rb +296 -0
- data/lib/tapyrus/key_path.rb +26 -0
- data/lib/tapyrus/logger.rb +42 -0
- data/lib/tapyrus/merkle_tree.rb +149 -0
- data/lib/tapyrus/message/addr.rb +35 -0
- data/lib/tapyrus/message/base.rb +28 -0
- data/lib/tapyrus/message/block.rb +46 -0
- data/lib/tapyrus/message/block_transaction_request.rb +45 -0
- data/lib/tapyrus/message/block_transactions.rb +31 -0
- data/lib/tapyrus/message/block_txn.rb +27 -0
- data/lib/tapyrus/message/cmpct_block.rb +42 -0
- data/lib/tapyrus/message/error.rb +10 -0
- data/lib/tapyrus/message/fee_filter.rb +27 -0
- data/lib/tapyrus/message/filter_add.rb +28 -0
- data/lib/tapyrus/message/filter_clear.rb +17 -0
- data/lib/tapyrus/message/filter_load.rb +39 -0
- data/lib/tapyrus/message/get_addr.rb +17 -0
- data/lib/tapyrus/message/get_block_txn.rb +27 -0
- data/lib/tapyrus/message/get_blocks.rb +29 -0
- data/lib/tapyrus/message/get_data.rb +21 -0
- data/lib/tapyrus/message/get_headers.rb +28 -0
- data/lib/tapyrus/message/header_and_short_ids.rb +57 -0
- data/lib/tapyrus/message/headers.rb +35 -0
- data/lib/tapyrus/message/headers_parser.rb +24 -0
- data/lib/tapyrus/message/inv.rb +21 -0
- data/lib/tapyrus/message/inventories_parser.rb +23 -0
- data/lib/tapyrus/message/inventory.rb +51 -0
- data/lib/tapyrus/message/mem_pool.rb +17 -0
- data/lib/tapyrus/message/merkle_block.rb +42 -0
- data/lib/tapyrus/message/network_addr.rb +63 -0
- data/lib/tapyrus/message/not_found.rb +21 -0
- data/lib/tapyrus/message/ping.rb +30 -0
- data/lib/tapyrus/message/pong.rb +26 -0
- data/lib/tapyrus/message/prefilled_tx.rb +29 -0
- data/lib/tapyrus/message/reject.rb +46 -0
- data/lib/tapyrus/message/send_cmpct.rb +43 -0
- data/lib/tapyrus/message/send_headers.rb +16 -0
- data/lib/tapyrus/message/tx.rb +30 -0
- data/lib/tapyrus/message/ver_ack.rb +17 -0
- data/lib/tapyrus/message/version.rb +69 -0
- data/lib/tapyrus/message.rb +70 -0
- data/lib/tapyrus/mnemonic/wordlist/chinese_simplified.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/chinese_traditional.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/english.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/french.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/italian.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/japanese.txt +2048 -0
- data/lib/tapyrus/mnemonic/wordlist/spanish.txt +2048 -0
- data/lib/tapyrus/mnemonic.rb +77 -0
- data/lib/tapyrus/network/connection.rb +73 -0
- data/lib/tapyrus/network/message_handler.rb +241 -0
- data/lib/tapyrus/network/peer.rb +223 -0
- data/lib/tapyrus/network/peer_discovery.rb +42 -0
- data/lib/tapyrus/network/pool.rb +135 -0
- data/lib/tapyrus/network.rb +13 -0
- data/lib/tapyrus/node/cli.rb +112 -0
- data/lib/tapyrus/node/configuration.rb +38 -0
- data/lib/tapyrus/node/spv.rb +79 -0
- data/lib/tapyrus/node.rb +7 -0
- data/lib/tapyrus/opcodes.rb +178 -0
- data/lib/tapyrus/out_point.rb +44 -0
- data/lib/tapyrus/rpc/http_server.rb +65 -0
- data/lib/tapyrus/rpc/request_handler.rb +150 -0
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +72 -0
- data/lib/tapyrus/rpc.rb +7 -0
- data/lib/tapyrus/script/multisig.rb +92 -0
- data/lib/tapyrus/script/script.rb +551 -0
- data/lib/tapyrus/script/script_error.rb +111 -0
- data/lib/tapyrus/script/script_interpreter.rb +668 -0
- data/lib/tapyrus/script/tx_checker.rb +81 -0
- data/lib/tapyrus/script_witness.rb +38 -0
- data/lib/tapyrus/secp256k1/native.rb +174 -0
- data/lib/tapyrus/secp256k1/ruby.rb +123 -0
- data/lib/tapyrus/secp256k1.rb +12 -0
- data/lib/tapyrus/slip39/share.rb +122 -0
- data/lib/tapyrus/slip39/sss.rb +245 -0
- data/lib/tapyrus/slip39/wordlist/english.txt +1024 -0
- data/lib/tapyrus/slip39.rb +93 -0
- data/lib/tapyrus/store/chain_entry.rb +67 -0
- data/lib/tapyrus/store/db/level_db.rb +98 -0
- data/lib/tapyrus/store/db.rb +9 -0
- data/lib/tapyrus/store/spv_chain.rb +101 -0
- data/lib/tapyrus/store.rb +9 -0
- data/lib/tapyrus/tx.rb +347 -0
- data/lib/tapyrus/tx_in.rb +89 -0
- data/lib/tapyrus/tx_out.rb +74 -0
- data/lib/tapyrus/util.rb +133 -0
- data/lib/tapyrus/validation.rb +115 -0
- data/lib/tapyrus/version.rb +3 -0
- data/lib/tapyrus/wallet/account.rb +151 -0
- data/lib/tapyrus/wallet/base.rb +162 -0
- data/lib/tapyrus/wallet/db.rb +81 -0
- data/lib/tapyrus/wallet/master_key.rb +110 -0
- data/lib/tapyrus/wallet.rb +8 -0
- data/lib/tapyrus.rb +219 -0
- data/tapyrusrb.conf.sample +0 -0
- data/tapyrusrb.gemspec +47 -0
- 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,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
|