tapyrus 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -14
  3. data/exe/tapyrusrbd +2 -2
  4. data/lib/openassets/util.rb +2 -4
  5. data/lib/schnorr.rb +83 -0
  6. data/lib/schnorr/signature.rb +38 -0
  7. data/lib/tapyrus.rb +9 -11
  8. data/lib/tapyrus/block.rb +1 -32
  9. data/lib/tapyrus/block_header.rb +7 -6
  10. data/lib/tapyrus/chain_params.rb +13 -26
  11. data/lib/tapyrus/chainparams/{testnet.yml → dev.yml} +7 -9
  12. data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -10
  13. data/lib/tapyrus/constants.rb +12 -34
  14. data/lib/tapyrus/ext.rb +5 -0
  15. data/lib/tapyrus/ext/json_parser.rb +47 -0
  16. data/lib/tapyrus/ext_key.rb +5 -10
  17. data/lib/tapyrus/key.rb +57 -29
  18. data/lib/tapyrus/message.rb +2 -2
  19. data/lib/tapyrus/message/base.rb +1 -0
  20. data/lib/tapyrus/message/block.rb +3 -3
  21. data/lib/tapyrus/message/cmpct_block.rb +3 -5
  22. data/lib/tapyrus/message/tx.rb +2 -2
  23. data/lib/tapyrus/network/peer.rb +1 -15
  24. data/lib/tapyrus/node/cli.rb +15 -11
  25. data/lib/tapyrus/node/configuration.rb +1 -1
  26. data/lib/tapyrus/node/spv.rb +1 -1
  27. data/lib/tapyrus/opcodes.rb +5 -0
  28. data/lib/tapyrus/out_point.rb +1 -1
  29. data/lib/tapyrus/rpc/request_handler.rb +3 -3
  30. data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
  31. data/lib/tapyrus/script/color.rb +79 -0
  32. data/lib/tapyrus/script/multisig.rb +0 -27
  33. data/lib/tapyrus/script/script.rb +74 -89
  34. data/lib/tapyrus/script/script_error.rb +8 -14
  35. data/lib/tapyrus/script/script_interpreter.rb +65 -86
  36. data/lib/tapyrus/script/tx_checker.rb +16 -4
  37. data/lib/tapyrus/secp256k1.rb +1 -0
  38. data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
  39. data/lib/tapyrus/secp256k1/ruby.rb +5 -31
  40. data/lib/tapyrus/store/chain_entry.rb +1 -0
  41. data/lib/tapyrus/tx.rb +18 -160
  42. data/lib/tapyrus/tx_in.rb +4 -11
  43. data/lib/tapyrus/tx_out.rb +2 -1
  44. data/lib/tapyrus/util.rb +8 -0
  45. data/lib/tapyrus/validation.rb +1 -6
  46. data/lib/tapyrus/version.rb +1 -1
  47. data/lib/tapyrus/wallet/account.rb +1 -0
  48. data/lib/tapyrus/wallet/master_key.rb +1 -0
  49. data/tapyrusrb.gemspec +3 -3
  50. metadata +42 -39
  51. data/lib/tapyrus/chainparams/regtest.yml +0 -38
  52. data/lib/tapyrus/descriptor.rb +0 -147
  53. data/lib/tapyrus/script_witness.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73863e0b35030aee45baa96661f5bbd0322e78381be022d8372e620f8cd8668c
4
- data.tar.gz: 3f6378420bb9940a4829e378bb9975c67f5e5c5b61e4c5e077d85f83e0f59715
3
+ metadata.gz: 1625f752a1848cec88c87af856ade34417b9f8aa618ee31db66387e60d8e7b8b
4
+ data.tar.gz: b30ed36b3f4e74f96b57866261b0be40c74883dcfa6605d68a73da99316bb746
5
5
  SHA512:
6
- metadata.gz: 5ffa34b251d363ade9355430980d50d233c104071b15f46535fee2009b8826f4b163891e384cec997b72e2680387cd3d200b55a41c4ebda8c359fdf6f965f091
7
- data.tar.gz: c3860a9b13c2f43ef55b47cb985eb9467d5c4fdf9f1436b6c0e97b9a1b04a2ecdcd726e0103baa5ccd302753cc460f0240c0cd76c32cc08347e46e997e0ec7d6
6
+ metadata.gz: 743afb61fcd03ab089bf3124e007d23789b87eac5f35176b3430966814b4ada178f49960dba7240df5f02d7bdfd4b9ef9639c0ca7a9c26d89f9abd498c0ee5ce
7
+ data.tar.gz: 1300203703bd438cf1e5a54967f9dcc1ee04d7a698a1678ad24b4ef8f3c18b568993790c40de537655d8b20fb06dec35dfaaeef8fe7d1fcc2886366876e18501
data/README.md CHANGED
@@ -65,29 +65,21 @@ And then add to your .rb file:
65
65
 
66
66
  The parameters of the blockchain are managed by `Tapyrus::ChainParams`. Switch chain parameters as follows:
67
67
 
68
- * mainnet
68
+ * prod
69
69
 
70
70
  ```ruby
71
- Tapyrus.chain_params = :mainnet
71
+ Tapyrus.chain_params = :prod
72
72
  ```
73
73
 
74
- This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/mainnet.yml.
74
+ This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/prod.yml.
75
75
 
76
- * testnet
76
+ * dev
77
77
 
78
78
  ```ruby
79
- Tapyrus.chain_params = :testnet
79
+ Tapyrus.chain_params = :dev
80
80
  ```
81
81
 
82
- This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/testnet.yml.
83
-
84
- * regtest
85
-
86
- ```ruby
87
- Tapyrus.chain_params = :regtest
88
- ```
89
-
90
- This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/regtest.yml.
82
+ This parameter is described in https://github.com/chaintope/tapyrusrb/blob/master/lib/tapyrus/chainparams/dev.yml.
91
83
 
92
84
  ## Contributing
93
85
 
@@ -16,11 +16,11 @@ end
16
16
 
17
17
  class Tapyrusrbd < Thor
18
18
 
19
- class_option :network, aliases: '-n', default: :mainnet
19
+ class_option :network, aliases: '-n', default: :prod
20
20
 
21
21
  desc 'start', 'start tapyrusrb daemon.'
22
22
  def start
23
- network = options['network'] ? options['network'].to_sym : :mainnet
23
+ network = options['network'] ? options['network'].to_sym : :prod
24
24
  Tapyrus.chain_params = network
25
25
  FileUtils.mkdir_p(Tapyrus.base_dir)
26
26
  execute_daemon(['start', network: network])
@@ -18,10 +18,8 @@ module OpenAssets
18
18
  end
19
19
 
20
20
  def oa_version_byte
21
- case Tapyrus.chain_params.network
22
- when 'mainnet' then OA_VERSION_BYTE
23
- when 'testnet', 'regtest' then OA_VERSION_BYTE_TESTNET
24
- end
21
+ return OA_VERSION_BYTE if Tapyrus.chain_params.prod?
22
+ return OA_VERSION_BYTE_TESTNET if Tapyrus.chain_params.dev?
25
23
  end
26
24
  end
27
25
  end
@@ -0,0 +1,83 @@
1
+ module Schnorr
2
+ autoload :Signature, 'schnorr/signature'
3
+
4
+ module_function
5
+
6
+ GROUP = ECDSA::Group::Secp256k1
7
+ ALGO16 = 'SCHNORR + SHA256'
8
+
9
+ # Generate schnorr signature.
10
+ # @param message (String) A message to be signed with binary format.
11
+ # @param private_key (Integer) The private key.
12
+ # (The number of times to add the generator point to itself to get the public key.)
13
+ # @return (Schnorr::Signature)
14
+ def sign(message, private_key)
15
+ raise 'The message must be a 32-byte array.' unless message.bytesize == 32
16
+ raise 'private_key is zero or over the curve order.' if private_key == 0 || private_key >= GROUP.order
17
+
18
+ p = GROUP.new_point(private_key)
19
+ secret = ECDSA::Format::IntegerOctetString.encode(private_key, GROUP.byte_length)
20
+ secret = secret + message + ALGO16
21
+ nonce = Tapyrus::Secp256k1::RFC6979.generate_rfc6979_nonce(secret, '')
22
+
23
+ k0 = nonce % GROUP.order
24
+ raise 'Creation of signature failed. k is zero' if k0.zero?
25
+
26
+ r = GROUP.new_point(k0)
27
+ k = ECDSA::PrimeField.jacobi(r.y, GROUP.field.prime) == 1 ? k0 : GROUP.order - k0
28
+
29
+ e = create_challenge(r.x, p, message)
30
+
31
+ Schnorr::Signature.new(r.x, (k + e * private_key) % GROUP.order)
32
+ end
33
+
34
+ # Verifies the given {Signature} and returns true if it is valid.
35
+ # @param message (String) A message to be signed with binary format.
36
+ # @param public_key (String) The public key with binary format.
37
+ # @param signature (String) The signature with binary format.
38
+ # @return (Boolean) whether signature is valid.
39
+ def valid_sig?(message, signature, public_key)
40
+ check_sig!(message, signature, public_key)
41
+ rescue InvalidSignatureError, ECDSA::Format::DecodeError
42
+ false
43
+ end
44
+
45
+ # Verifies the given {Signature} and raises an {InvalidSignatureError} if it is invalid.
46
+ # @param message (String) A message to be signed with binary format.
47
+ # @param public_key (String) The public key with binary format.
48
+ # @param signature (String) The signature with binary format.
49
+ # @return (Boolean)
50
+ def check_sig!(message, signature, public_key)
51
+ sig = Schnorr::Signature.decode(signature)
52
+ pubkey = ECDSA::Format::PointOctetString.decode(public_key, GROUP)
53
+ field = GROUP.field
54
+
55
+ raise Schnorr::InvalidSignatureError, 'Invalid signature: r is not in the field.' unless field.include?(sig.r)
56
+ raise Schnorr::InvalidSignatureError, 'Invalid signature: s is not in the field.' unless field.include?(sig.s)
57
+ raise Schnorr::InvalidSignatureError, 'Invalid signature: r is zero.' if sig.r.zero?
58
+ raise Schnorr::InvalidSignatureError, 'Invalid signature: s is zero.' if sig.s.zero?
59
+ raise Schnorr::InvalidSignatureError, 'Invalid signature: r is larger than field size.' if sig.r >= field.prime
60
+ raise Schnorr::InvalidSignatureError, 'Invalid signature: s is larger than group order.' if sig.s >= GROUP.order
61
+
62
+ e = create_challenge(sig.r, pubkey, message)
63
+
64
+ r = GROUP.new_point(sig.s) + pubkey.multiply_by_scalar(e).negate
65
+
66
+ if r.infinity? || r.x != sig.r || ECDSA::PrimeField.jacobi(r.y, GROUP.field.prime) != 1
67
+ raise Schnorr::InvalidSignatureError, 'signature verification failed.'
68
+ end
69
+
70
+ true
71
+ end
72
+
73
+ # create signature digest.
74
+ # @param (Integer) x a x coordinate for R.
75
+ # @param (ECDSA::Point) p a public key.
76
+ # @return (Integer) digest e.
77
+ def create_challenge(x, p, message)
78
+ r_x = ECDSA::Format::IntegerOctetString.encode(x, GROUP.byte_length)
79
+ p_str= ECDSA::Format::PointOctetString.encode(p, compression:true)
80
+ (ECDSA.normalize_digest(Digest::SHA256.digest(r_x + p_str + message), GROUP.bit_length)) % GROUP.order
81
+ end
82
+
83
+ end
@@ -0,0 +1,38 @@
1
+ module Schnorr
2
+
3
+ class InvalidSignatureError < StandardError; end
4
+
5
+ # Instances of this class represents Schnorr signatures,
6
+ # which are simply a pair of integers named `r` and `s`.
7
+ class Signature
8
+
9
+ attr_reader :r
10
+ attr_reader :s
11
+
12
+ # @param r (Integer) the value of r.
13
+ # @param s (Integer) the value of s.
14
+ def initialize(r, s)
15
+ @r, @s = r, s
16
+ r.is_a?(Integer) or raise ArgumentError, 'r is not an integer.'
17
+ s.is_a?(Integer) or raise ArgumentError, 's is not an integer.'
18
+ end
19
+
20
+ # Parse a string to a {Signature}.
21
+ # @param string (String) signature string with binary format.
22
+ # @return (Signature) signature instance.
23
+ def self.decode(string)
24
+ raise InvalidSignatureError, 'Invalid schnorr signature length.' unless string.bytesize == 64
25
+ r = string[0...32].unpack('H*').first.to_i(16)
26
+ s = string[32..-1].unpack('H*').first.to_i(16)
27
+ new(r, s)
28
+ end
29
+
30
+ # Encode signature to string.
31
+ # @return (String) encoded signature.
32
+ def encode
33
+ ECDSA::Format::IntegerOctetString.encode(r, 32) + ECDSA::Format::IntegerOctetString.encode(s, 32)
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -6,14 +6,15 @@ require 'eventmachine'
6
6
  require 'ecdsa'
7
7
  require 'securerandom'
8
8
  require 'json'
9
- require 'bech32'
10
9
  require 'ffi'
11
10
  require 'observer'
12
11
  require 'tmpdir'
13
12
  require_relative 'openassets'
13
+ require_relative 'schnorr'
14
14
 
15
15
  module Tapyrus
16
16
 
17
+ autoload :Ext, 'tapyrus/ext'
17
18
  autoload :Util, 'tapyrus/util'
18
19
  autoload :ChainParams, 'tapyrus/chain_params'
19
20
  autoload :Message, 'tapyrus/message'
@@ -29,7 +30,6 @@ module Tapyrus
29
30
  autoload :TxIn, 'tapyrus/tx_in'
30
31
  autoload :TxOut, 'tapyrus/tx_out'
31
32
  autoload :OutPoint, 'tapyrus/out_point'
32
- autoload :ScriptWitness, 'tapyrus/script_witness'
33
33
  autoload :MerkleTree, 'tapyrus/merkle_tree'
34
34
  autoload :Key, 'tapyrus/key'
35
35
  autoload :ExtKey, 'tapyrus/ext_key'
@@ -46,18 +46,18 @@ module Tapyrus
46
46
  autoload :Wallet, 'tapyrus/wallet'
47
47
  autoload :BloomFilter, 'tapyrus/bloom_filter'
48
48
  autoload :KeyPath, 'tapyrus/key_path'
49
- autoload :Descriptor, 'tapyrus/descriptor'
50
49
  autoload :SLIP39, 'tapyrus/slip39'
50
+ autoload :Color, 'tapyrus/script/color'
51
51
 
52
52
  require_relative 'tapyrus/constants'
53
53
 
54
54
  extend Util
55
55
 
56
- @chain_param = :mainnet
56
+ @chain_param = :prod
57
57
 
58
58
  # set tapyrus network chain params
59
59
  def self.chain_params=(name)
60
- raise "chain params for #{name} is not defined." unless %i(mainnet testnet regtest).include?(name.to_sym)
60
+ raise "chain params for #{name} is not defined." unless %i(prod dev).include?(name.to_sym)
61
61
  @current_chain = nil
62
62
  @chain_param = name.to_sym
63
63
  end
@@ -66,12 +66,10 @@ module Tapyrus
66
66
  def self.chain_params
67
67
  return @current_chain if @current_chain
68
68
  case @chain_param
69
- when :mainnet
70
- @current_chain = Tapyrus::ChainParams.mainnet
71
- when :testnet
72
- @current_chain = Tapyrus::ChainParams.testnet
73
- when :regtest
74
- @current_chain = Tapyrus::ChainParams.regtest
69
+ when :prod
70
+ @current_chain = Tapyrus::ChainParams.prod
71
+ when :dev
72
+ @current_chain = Tapyrus::ChainParams.dev
75
73
  end
76
74
  @current_chain
77
75
  end
@@ -21,23 +21,6 @@ module Tapyrus
21
21
  header.block_hash
22
22
  end
23
23
 
24
- # calculate block weight
25
- def weight
26
- stripped_size * (WITNESS_SCALE_FACTOR - 1) + size
27
- end
28
-
29
- # calculate total size (include witness data.)
30
- def size
31
- 80 + Tapyrus.pack_var_int(transactions.size).bytesize +
32
- transactions.inject(0){|sum, tx| sum + (tx.witness? ? tx.serialize_witness_format.bytesize : tx.serialize_old_format.bytesize)}
33
- end
34
-
35
- # calculate base size (not include witness data.)
36
- def stripped_size
37
- 80 + Tapyrus.pack_var_int(transactions.size).bytesize +
38
- transactions.inject(0){|sum, tx| sum + tx.serialize_old_format.bytesize}
39
- end
40
-
41
24
  # check the merkle root in the block header matches merkle root calculated from tx list.
42
25
  def valid_merkle_root?
43
26
  calculate_merkle_root == header.merkle_root
@@ -48,24 +31,10 @@ module Tapyrus
48
31
  Tapyrus::MerkleTree.build_from_leaf(transactions.map(&:tx_hash)).merkle_root
49
32
  end
50
33
 
51
- # check the witness commitment in coinbase tx matches witness commitment calculated from tx list.
52
- def valid_witness_commitment?
53
- transactions[0].witness_commitment == calculate_witness_commitment
54
- end
55
-
56
- # calculate witness commitment from tx list.
57
- def calculate_witness_commitment
58
- witness_hashes = [COINBASE_WTXID]
59
- witness_hashes += (transactions[1..-1].map(&:witness_hash))
60
- reserved_value = transactions[0].inputs[0].script_witness.stack.map(&:bth).join
61
- root_hash = Tapyrus::MerkleTree.build_from_leaf(witness_hashes).merkle_root
62
- Tapyrus.double_sha256([root_hash + reserved_value].pack('H*')).bth
63
- end
64
-
65
34
  # return this block height. block height is included in coinbase.
66
35
  # if block version under 1, height does not include in coinbase, so return nil.
67
36
  def height
68
- return nil if header.version < 2
37
+ return nil if header.features < 2
69
38
  coinbase_tx = transactions[0]
70
39
  return nil unless coinbase_tx.coinbase_tx?
71
40
  buf = StringIO.new(coinbase_tx.inputs[0].script_sig.to_payload)
@@ -2,16 +2,17 @@ module Tapyrus
2
2
 
3
3
  # Block Header
4
4
  class BlockHeader
5
+ include Tapyrus::HexConverter
5
6
 
6
- attr_accessor :version
7
+ attr_accessor :features
7
8
  attr_accessor :prev_hash
8
9
  attr_accessor :merkle_root
9
10
  attr_accessor :time # unix timestamp
10
11
  attr_accessor :bits
11
12
  attr_accessor :nonce
12
13
 
13
- def initialize(version, prev_hash, merkle_root, time, bits, nonce)
14
- @version = version
14
+ def initialize(features, prev_hash, merkle_root, time, bits, nonce)
15
+ @features = features
15
16
  @prev_hash = prev_hash
16
17
  @merkle_root = merkle_root
17
18
  @time = time
@@ -20,12 +21,12 @@ module Tapyrus
20
21
  end
21
22
 
22
23
  def self.parse_from_payload(payload)
23
- version, prev_hash, merkle_root, time, bits, nonce = payload.unpack('Va32a32VVV')
24
- new(version, prev_hash.bth, merkle_root.bth, time, bits, nonce)
24
+ features, prev_hash, merkle_root, time, bits, nonce = payload.unpack('Va32a32VVV')
25
+ new(features, prev_hash.bth, merkle_root.bth, time, bits, nonce)
25
26
  end
26
27
 
27
28
  def to_payload
28
- [version, prev_hash.htb, merkle_root.htb, time, bits, nonce].pack('Va32a32VVV')
29
+ [features, prev_hash.htb, merkle_root.htb, time, bits, nonce].pack('Va32a32VVV')
29
30
  end
30
31
 
31
32
  # compute difficulty target from bits.
@@ -10,7 +10,8 @@ module Tapyrus
10
10
  attr_reader :message_magic
11
11
  attr_reader :address_version
12
12
  attr_reader :p2sh_version
13
- attr_reader :bech32_hrp
13
+ attr_reader :cp2pkh_version
14
+ attr_reader :cp2sh_version
14
15
  attr_reader :privkey_version
15
16
  attr_reader :extended_privkey_version
16
17
  attr_reader :extended_pubkey_version
@@ -23,6 +24,7 @@ module Tapyrus
23
24
  attr_reader :bip84_pubkey_p2wsh_version
24
25
  attr_reader :bip84_privkey_p2wsh_version
25
26
  attr_reader :default_port
27
+ attr_reader :rpc_port
26
28
  attr_reader :protocol_version
27
29
  attr_reader :retarget_interval
28
30
  attr_reader :retarget_time
@@ -36,34 +38,23 @@ module Tapyrus
36
38
 
37
39
  attr_accessor :dust_relay_fee
38
40
 
39
- # fork coin id.
40
- attr_accessor :fork_id
41
41
 
42
- # mainnet genesis
43
- def self.mainnet
44
- init('mainnet')
42
+ # production genesis
43
+ def self.prod
44
+ init('prod')
45
45
  end
46
46
 
47
- # testnet genesis
48
- def self.testnet
49
- init('testnet')
47
+ # development genesis
48
+ def self.dev
49
+ init('dev')
50
50
  end
51
51
 
52
- # regtest genesis
53
- def self.regtest
54
- init('regtest')
52
+ def prod?
53
+ network == 'prod'
55
54
  end
56
55
 
57
- def mainnet?
58
- network == 'mainnet'
59
- end
60
-
61
- def testnet?
62
- network == 'testnet'
63
- end
64
-
65
- def regtest?
66
- network == 'regtest'
56
+ def dev?
57
+ network == 'dev'
67
58
  end
68
59
 
69
60
  def genesis_block
@@ -73,10 +64,6 @@ module Tapyrus
73
64
  Tapyrus::Block.new(header)
74
65
  end
75
66
 
76
- # whether fork coin.
77
- def fork_chain?
78
- !fork_id.nil?
79
- end
80
67
 
81
68
  def self.init(name)
82
69
  i = YAML.load(File.open("#{__dir__}/chainparams/#{name}.yml"))
@@ -1,10 +1,11 @@
1
1
  --- !ruby/object:Tapyrus::ChainParams
2
- network: "testnet"
2
+ network: "dev"
3
3
  magic_head: "0b110907"
4
- message_magic: "Bitcoin Signed Message:\n"
4
+ message_magic: "Tapyrus Signed Message:\n"
5
5
  address_version: "6f"
6
6
  p2sh_version: "c4"
7
- bech32_hrp: 'tb'
7
+ cp2pkh_version: "70"
8
+ cp2sh_version: "c5"
8
9
  privkey_version: "ef"
9
10
  extended_privkey_version: "04358394"
10
11
  extended_pubkey_version: "043587cf"
@@ -16,8 +17,9 @@ bip84_pubkey_p2wpkh_version: "045f1cf6"
16
17
  bip84_pubkey_p2wsh_version: "02575483"
17
18
  bip84_privkey_p2wpkh_version: "045f18bc"
18
19
  bip84_privkey_p2wsh_version: "02575048"
19
- default_port: 18333
20
- protocol_version: 70013
20
+ default_port: 12383
21
+ rpc_port: 12381
22
+ protocol_version: 10000
21
23
  retarget_interval: 2016
22
24
  retarget_time: 1209600 # 2 weeks
23
25
  target_spacing: 600 # block interval
@@ -26,10 +28,6 @@ bip34_height: 227931
26
28
  genesis_hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
27
29
  proof_of_work_limit: 0x1d00ffff
28
30
  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
31
  genesis:
34
32
  hash: "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"
35
33
  merkle_root: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"